AnnotatedElementUtils adapts post-processed values to AnnotationAttributes as well
Issue: SPR-12065
This commit is contained in:
parent
a635c36470
commit
ef51d4dbdb
|
|
@ -28,6 +28,7 @@ import org.junit.Test;
|
|||
import org.springframework.aop.scope.ScopedObject;
|
||||
import org.springframework.aop.scope.ScopedProxyUtils;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
|
@ -39,6 +40,7 @@ import org.springframework.beans.factory.support.RootBeanDefinition;
|
|||
import org.springframework.context.annotation.componentscan.simple.SimpleComponent;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.core.io.DescriptiveResource;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.tests.sample.beans.ITestBean;
|
||||
import org.springframework.tests.sample.beans.TestBean;
|
||||
import org.springframework.util.Assert;
|
||||
|
|
@ -126,19 +128,33 @@ public class ConfigurationClassPostProcessorTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void postProcessorWorksWithComposedConfigurationWithAttributeOverridesUsingReflection() {
|
||||
public void postProcessorWorksWithComposedConfigurationWithAttributeOverrideForBasePackageUsingReflection() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
||||
ComposedConfigurationWithAttributeOverridesClass.class);
|
||||
ComposedConfigurationWithAttributeOverrideForBasePackage.class);
|
||||
assertSupportForComposedAnnotation(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessorWorksWithComposedConfigurationWithAttributeOverridesUsingAsm() {
|
||||
public void postProcessorWorksWithComposedConfigurationWithAttributeOverrideForBasePackageUsingAsm() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
||||
ComposedConfigurationWithAttributeOverridesClass.class.getName());
|
||||
ComposedConfigurationWithAttributeOverrideForBasePackage.class.getName());
|
||||
assertSupportForComposedAnnotation(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessorWorksWithComposedConfigurationWithAttributeOverrideForExcludeFilterUsingReflection() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
||||
ComposedConfigurationWithAttributeOverrideForExcludeFilter.class);
|
||||
assertSupportForComposedAnnotationWithExclude(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessorWorksWithComposedConfigurationWithAttributeOverrideForExcludeFilterUsingAsm() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
||||
ComposedConfigurationWithAttributeOverrideForExcludeFilter.class.getName());
|
||||
assertSupportForComposedAnnotationWithExclude(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessorWorksWithComposedComposedConfigurationWithAttributeOverridesUsingReflection() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
||||
|
|
@ -181,6 +197,29 @@ public class ConfigurationClassPostProcessorTests {
|
|||
assertSupportForComposedAnnotation(beanDefinition);
|
||||
}
|
||||
|
||||
private void assertSupportForComposedAnnotation(RootBeanDefinition beanDefinition) {
|
||||
beanFactory.registerBeanDefinition("config", beanDefinition);
|
||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||
pp.setEnvironment(new StandardEnvironment());
|
||||
pp.postProcessBeanFactory(beanFactory);
|
||||
SimpleComponent simpleComponent = beanFactory.getBean(SimpleComponent.class);
|
||||
assertNotNull(simpleComponent);
|
||||
}
|
||||
|
||||
private void assertSupportForComposedAnnotationWithExclude(RootBeanDefinition beanDefinition) {
|
||||
beanFactory.registerBeanDefinition("config", beanDefinition);
|
||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||
pp.setEnvironment(new StandardEnvironment());
|
||||
pp.postProcessBeanFactory(beanFactory);
|
||||
try {
|
||||
beanFactory.getBean(SimpleComponent.class);
|
||||
fail("Should have thrown NoSuchBeanDefinitionException");
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessorOverridesNonApplicationBeanDefinitions() {
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(TestBean.class);
|
||||
|
|
@ -378,15 +417,6 @@ public class ConfigurationClassPostProcessorTests {
|
|||
assertSame(beanFactory.getBean("genericRepo"), beanFactory.getBean("repoConsumer"));
|
||||
}
|
||||
|
||||
private void assertSupportForComposedAnnotation(RootBeanDefinition beanDefinition) {
|
||||
beanFactory.registerBeanDefinition("config", beanDefinition);
|
||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||
pp.setEnvironment(new StandardEnvironment());
|
||||
pp.postProcessBeanFactory(beanFactory);
|
||||
SimpleComponent simpleComponent = beanFactory.getBean(SimpleComponent.class);
|
||||
assertNotNull(simpleComponent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelfReferenceExclusionForFactoryMethodOnSameBean() {
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
|
|
@ -718,10 +748,17 @@ public class ConfigurationClassPostProcessorTests {
|
|||
public static @interface ComposedConfigurationWithAttributeOverrides {
|
||||
|
||||
String[] basePackages() default {};
|
||||
|
||||
ComponentScan.Filter[] excludeFilters() default {};
|
||||
}
|
||||
|
||||
@ComposedConfigurationWithAttributeOverrides(basePackages = "org.springframework.context.annotation.componentscan.simple")
|
||||
public static class ComposedConfigurationWithAttributeOverridesClass {
|
||||
public static class ComposedConfigurationWithAttributeOverrideForBasePackage {
|
||||
}
|
||||
|
||||
@ComposedConfigurationWithAttributeOverrides(basePackages = "org.springframework.context.annotation.componentscan.simple",
|
||||
excludeFilters = @ComponentScan.Filter(Component.class))
|
||||
public static class ComposedConfigurationWithAttributeOverrideForExcludeFilter {
|
||||
}
|
||||
|
||||
@ComposedConfigurationWithAttributeOverrides
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ public class AnnotatedElementUtils {
|
|||
public static Set<String> getMetaAnnotationTypes(AnnotatedElement element, String annotationType) {
|
||||
final Set<String> types = new LinkedHashSet<String>();
|
||||
process(element, annotationType, false, new Processor<Object>() {
|
||||
|
||||
@Override
|
||||
public Object process(Annotation annotation, int metaDepth) {
|
||||
if (metaDepth > 0) {
|
||||
|
|
@ -48,7 +47,6 @@ public class AnnotatedElementUtils {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcess(Annotation annotation, Object result) {
|
||||
}
|
||||
|
|
@ -101,7 +99,7 @@ public class AnnotatedElementUtils {
|
|||
if (!AnnotationUtils.VALUE.equals(key)) {
|
||||
Object value = AnnotationUtils.getValue(annotation, key);
|
||||
if (value != null) {
|
||||
result.put(key, value);
|
||||
result.put(key, AnnotationUtils.adaptValue(value, classValuesAsString, nestedAnnotationsAsMap));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -109,8 +107,7 @@ public class AnnotatedElementUtils {
|
|||
});
|
||||
}
|
||||
|
||||
public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element,
|
||||
String annotationType) {
|
||||
public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element, String annotationType) {
|
||||
return getAllAnnotationAttributes(element, annotationType, false, false);
|
||||
}
|
||||
|
||||
|
|
@ -122,8 +119,8 @@ public class AnnotatedElementUtils {
|
|||
@Override
|
||||
public Void process(Annotation annotation, int metaDepth) {
|
||||
if (annotation.annotationType().getName().equals(annotationType)) {
|
||||
for (Map.Entry<String, Object> entry : AnnotationUtils.getAnnotationAttributes(annotation,
|
||||
classValuesAsString, nestedAnnotationsAsMap).entrySet()) {
|
||||
for (Map.Entry<String, Object> entry : AnnotationUtils.getAnnotationAttributes(
|
||||
annotation, classValuesAsString, nestedAnnotationsAsMap).entrySet()) {
|
||||
attributes.add(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
|
@ -163,7 +160,7 @@ public class AnnotatedElementUtils {
|
|||
|
||||
try {
|
||||
return doProcess(element, annotationType, traverseClassHierarchy, processor,
|
||||
new HashSet<AnnotatedElement>(), 0);
|
||||
new HashSet<AnnotatedElement>(), 0);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalStateException("Failed to introspect annotations: " + element, ex);
|
||||
|
|
@ -199,8 +196,8 @@ public class AnnotatedElementUtils {
|
|||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
result = doProcess(annotation.annotationType(), annotationType, traverseClassHierarchy, processor,
|
||||
visited, metaDepth + 1);
|
||||
result = doProcess(annotation.annotationType(), annotationType, traverseClassHierarchy,
|
||||
processor, visited, metaDepth + 1);
|
||||
if (result != null) {
|
||||
processor.postProcess(annotation, result);
|
||||
return result;
|
||||
|
|
@ -210,7 +207,7 @@ public class AnnotatedElementUtils {
|
|||
for (Annotation annotation : annotations) {
|
||||
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) {
|
||||
T result = doProcess(annotation.annotationType(), annotationType, traverseClassHierarchy,
|
||||
processor, visited, metaDepth);
|
||||
processor, visited, metaDepth);
|
||||
if (result != null) {
|
||||
processor.postProcess(annotation, result);
|
||||
return result;
|
||||
|
|
@ -220,8 +217,7 @@ public class AnnotatedElementUtils {
|
|||
if (traverseClassHierarchy && element instanceof Class) {
|
||||
Class<?> superclass = ((Class<?>) element).getSuperclass();
|
||||
if (superclass != null && !superclass.equals(Object.class)) {
|
||||
T result = doProcess(superclass, annotationType, true, processor, visited,
|
||||
metaDepth);
|
||||
T result = doProcess(superclass, annotationType, true, processor, visited, metaDepth);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
|
|
@ -247,7 +243,7 @@ public class AnnotatedElementUtils {
|
|||
* will have a depth of 2.
|
||||
* @param annotation the annotation to process
|
||||
* @param metaDepth the depth of the annotation relative to the initial element
|
||||
* @return the result of the processing or {@code null} to continue
|
||||
* @return the result of the processing, or {@code null} to continue
|
||||
*/
|
||||
T process(Annotation annotation, int metaDepth);
|
||||
|
||||
|
|
|
|||
|
|
@ -578,34 +578,7 @@ public abstract class AnnotationUtils {
|
|||
if (method.getParameterTypes().length == 0 && method.getReturnType() != void.class) {
|
||||
try {
|
||||
Object value = method.invoke(annotation);
|
||||
if (classValuesAsString) {
|
||||
if (value instanceof Class) {
|
||||
value = ((Class<?>) value).getName();
|
||||
}
|
||||
else if (value instanceof Class[]) {
|
||||
Class<?>[] clazzArray = (Class[]) value;
|
||||
String[] newValue = new String[clazzArray.length];
|
||||
for (int i = 0; i < clazzArray.length; i++) {
|
||||
newValue[i] = clazzArray[i].getName();
|
||||
}
|
||||
value = newValue;
|
||||
}
|
||||
}
|
||||
if (nestedAnnotationsAsMap && value instanceof Annotation) {
|
||||
attrs.put(method.getName(),
|
||||
getAnnotationAttributes((Annotation) value, classValuesAsString, true));
|
||||
}
|
||||
else if (nestedAnnotationsAsMap && value instanceof Annotation[]) {
|
||||
Annotation[] realAnnotations = (Annotation[]) value;
|
||||
AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[realAnnotations.length];
|
||||
for (int i = 0; i < realAnnotations.length; i++) {
|
||||
mappedAnnotations[i] = getAnnotationAttributes(realAnnotations[i], classValuesAsString, true);
|
||||
}
|
||||
attrs.put(method.getName(), mappedAnnotations);
|
||||
}
|
||||
else {
|
||||
attrs.put(method.getName(), value);
|
||||
}
|
||||
attrs.put(method.getName(), adaptValue(value, classValuesAsString, nestedAnnotationsAsMap));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Could not obtain annotation attribute values", ex);
|
||||
|
|
@ -615,6 +588,48 @@ public abstract class AnnotationUtils {
|
|||
return attrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapt the given value according to the given class and nested annotation settings.
|
||||
* @param value the annotation attribute value
|
||||
* @param classValuesAsString whether to turn Class references into Strings (for
|
||||
* compatibility with {@link org.springframework.core.type.AnnotationMetadata}
|
||||
* or to preserve them as Class references
|
||||
* @param nestedAnnotationsAsMap whether to turn nested Annotation instances into
|
||||
* {@link AnnotationAttributes} maps (for compatibility with
|
||||
* {@link org.springframework.core.type.AnnotationMetadata} or to preserve them as
|
||||
* Annotation instances
|
||||
* @return the adapted value, or the original value if no adaptation is needed
|
||||
*/
|
||||
static Object adaptValue(Object value, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
|
||||
if (classValuesAsString) {
|
||||
if (value instanceof Class) {
|
||||
value = ((Class<?>) value).getName();
|
||||
}
|
||||
else if (value instanceof Class[]) {
|
||||
Class<?>[] clazzArray = (Class[]) value;
|
||||
String[] newValue = new String[clazzArray.length];
|
||||
for (int i = 0; i < clazzArray.length; i++) {
|
||||
newValue[i] = clazzArray[i].getName();
|
||||
}
|
||||
value = newValue;
|
||||
}
|
||||
}
|
||||
if (nestedAnnotationsAsMap && value instanceof Annotation) {
|
||||
return getAnnotationAttributes((Annotation) value, classValuesAsString, true);
|
||||
}
|
||||
else if (nestedAnnotationsAsMap && value instanceof Annotation[]) {
|
||||
Annotation[] realAnnotations = (Annotation[]) value;
|
||||
AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[realAnnotations.length];
|
||||
for (int i = 0; i < realAnnotations.length; i++) {
|
||||
mappedAnnotations[i] = getAnnotationAttributes(realAnnotations[i], classValuesAsString, true);
|
||||
}
|
||||
return mappedAnnotations;
|
||||
}
|
||||
else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the <em>value</em> of the {@code "value"} attribute of a
|
||||
* single-element Annotation, given an annotation instance.
|
||||
|
|
|
|||
Loading…
Reference in New Issue