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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,17 +18,21 @@ package org.springframework.core.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.core.annotation.AnnotationUtils.*;
|
||||
|
||||
@@ -37,6 +41,7 @@ import static org.springframework.core.annotation.AnnotationUtils.*;
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @author Chris Beams
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class AnnotationUtilsTests {
|
||||
|
||||
@@ -268,6 +273,19 @@ public class AnnotationUtilsTests {
|
||||
assertNotNull(order);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRepeatableFromMethod() throws Exception {
|
||||
Method method = InterfaceWithRepeated.class.getMethod("foo");
|
||||
Set<MyRepeatable> annotions = AnnotationUtils.getRepeatableAnnotation(method,
|
||||
MyRepeatableContainer.class, MyRepeatable.class);
|
||||
Set<String> values = new HashSet<String>();
|
||||
for (MyRepeatable myRepeatable : annotions) {
|
||||
values.add(myRepeatable.value());
|
||||
}
|
||||
assertThat(values, equalTo((Set<String>) new HashSet<String>(
|
||||
Arrays.asList("a", "b", "c", "meta"))));
|
||||
}
|
||||
|
||||
|
||||
@Component(value = "meta1")
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@@ -428,10 +446,46 @@ public class AnnotationUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
public static interface InterfaceWithRepeated {
|
||||
|
||||
@MyRepeatable("a")
|
||||
@MyRepeatableContainer({ @MyRepeatable("b"), @MyRepeatable("c") })
|
||||
@MyRepeatableMeta
|
||||
void foo();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@interface Transactional {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@interface MyRepeatableContainer {
|
||||
|
||||
MyRepeatable[] value();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Repeatable(MyRepeatableContainer.class)
|
||||
@interface MyRepeatable {
|
||||
|
||||
String value();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@MyRepeatable("meta")
|
||||
@interface MyRepeatableMeta {
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user