Add @PropertySources and ignoreResourceNotFound

Support repeatable @PropertySource annotations in Java 8 and add
@PropertySources container annotation for Java 6/7. Also add an
ignoreResourceNotFound attribute to @PropertySource allowing missing
property resources to be silently ignored.

This commit also introduces some generally useful methods to
AnnotationUtils for working with @Repeatable annotations.

Issue: SPR-8371
This commit is contained in:
Phillip Webb
2013-10-22 11:10:20 -07:00
parent 8917821e95
commit e95bd9e250
10 changed files with 364 additions and 52 deletions

View File

@@ -19,12 +19,18 @@ package org.springframework.core.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* General utility methods for working with annotations, handling bridge methods (which the compiler
@@ -43,6 +49,7 @@ import org.springframework.util.Assert;
* @author Sam Brannen
* @author Mark Fisher
* @author Chris Beams
* @author Phillip Webb
* @since 2.0
* @see java.lang.reflect.Method#getAnnotations()
* @see java.lang.reflect.Method#getAnnotation(Class)
@@ -117,6 +124,45 @@ public abstract class AnnotationUtils {
return getAnnotation((AnnotatedElement) resolvedMethod, annotationType);
}
/**
* Get the possibly repeating {@link Annotation}s of {@code annotationType} from the
* supplied {@link Method}. Deals with both a single direct annotation and repeating
* annotations nested within a containing annotation.
* <p>Correctly handles bridge {@link Method Methods} generated by the compiler.
* @param method the method to look for annotations on
* @param containerAnnotationType the class of the container that holds the annotations
* @param annotationType the annotation class to look for
* @return the annotations found
* @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method)
* @since 4.0
*/
public static <A extends Annotation> Set<A> getRepeatableAnnotation(Method method,
Class<? extends Annotation> containerAnnotationType, Class<A> annotationType) {
Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
return getRepeatableAnnotation((AnnotatedElement) resolvedMethod,
containerAnnotationType, annotationType);
}
/**
* Get the possibly repeating {@link Annotation}s of {@code annotationType} from the
* supplied {@link AnnotatedElement}. Deals with both a single direct annotation and
* repeating annotations nested within a containing annotation.
* <p>Correctly handles bridge {@link Method Methods} generated by the compiler.
* @param annotatedElement the element to look for annotations on
* @param containerAnnotationType the class of the container that holds the annotations
* @param annotationType the annotation class to look for
* @return the annotations found
* @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method)
* @since 4.0
*/
public static <A extends Annotation> Set<A> getRepeatableAnnotation(AnnotatedElement annotatedElement,
Class<? extends Annotation> containerAnnotationType, Class<A> annotationType) {
if (annotatedElement.getAnnotations().length == 0) {
return Collections.emptySet();
}
return new AnnotationCollector<A>(containerAnnotationType, annotationType).getResult(annotatedElement);
}
/**
* Get a single {@link Annotation} of {@code annotationType} from the supplied {@link Method},
* traversing its super methods if no annotation can be found on the given method itself.
@@ -521,4 +567,59 @@ public abstract class AnnotationUtils {
}
}
private static class AnnotationCollector<A extends Annotation> {
private final Class<? extends Annotation> containerAnnotationType;
private final Class<A> annotationType;
private final Set<AnnotatedElement> visited = new HashSet<AnnotatedElement>();
private final Set<A> result = new LinkedHashSet<A>();
public AnnotationCollector(Class<? extends Annotation> containerAnnotationType,
Class<A> annotationType) {
this.containerAnnotationType = containerAnnotationType;
this.annotationType = annotationType;
}
public Set<A> getResult(AnnotatedElement element) {
process(element);
return Collections.unmodifiableSet(this.result);
}
@SuppressWarnings("unchecked")
private void process(AnnotatedElement annotatedElement) {
if (this.visited.add(annotatedElement)) {
for (Annotation annotation : annotatedElement.getAnnotations()) {
if (ObjectUtils.nullSafeEquals(this.annotationType, annotation.annotationType())) {
this.result.add((A) annotation);
}
else if (ObjectUtils.nullSafeEquals(this.containerAnnotationType, annotation.annotationType())) {
result.addAll(Arrays.asList(getValue(annotation)));
}
else {
process(annotation.annotationType());
}
}
}
}
@SuppressWarnings("unchecked")
private A[] getValue(Annotation annotation) {
try {
Method method = annotation.annotationType().getDeclaredMethod("value");
return (A[]) method.invoke(annotation);
}
catch (Exception ex) {
throw new IllegalStateException("Unable to read value from repeating annotation container "
+ this.containerAnnotationType.getName(), ex);
}
}
}
}