Browse Source

Consistent meta-annotation attributes lookup through ASM

Issue: SPR-14257
pull/1059/merge
Juergen Hoeller 10 years ago
parent
commit
24f5f368b0
  1. 64
      spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassWithConditionTests.java
  2. 2
      spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java
  3. 2
      spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java
  4. 33
      spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java
  5. 7
      spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationReadingVisitorUtils.java
  6. 4
      spring-core/src/main/java/org/springframework/core/type/classreading/RecursiveAnnotationAttributesVisitor.java

64
spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassWithConditionTests.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2015 the original author or authors. * Copyright 2002-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -22,12 +22,10 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import java.util.Map; import java.util.Map;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.AnnotationMetadata;
@ -45,9 +43,6 @@ import static org.junit.Assert.*;
@SuppressWarnings("resource") @SuppressWarnings("resource")
public class ConfigurationClassWithConditionTests { public class ConfigurationClassWithConditionTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test @Test
public void conditionalOnMissingBeanMatch() throws Exception { public void conditionalOnMissingBeanMatch() throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
@ -94,13 +89,28 @@ public class ConfigurationClassWithConditionTests {
assertTrue(ctx.containsBean("bean")); assertTrue(ctx.containsBean("bean"));
} }
@Test
public void metaConditionalWithAsm() throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.registerBeanDefinition("config", new RootBeanDefinition(ConfigurationWithMetaCondition.class.getName()));
ctx.refresh();
assertTrue(ctx.containsBean("bean"));
}
@Test @Test
public void nonConfigurationClass() throws Exception { public void nonConfigurationClass() throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(NonConfigurationClass.class); ctx.register(NonConfigurationClass.class);
ctx.refresh(); ctx.refresh();
thrown.expect(NoSuchBeanDefinitionException.class); assertFalse(ctx.containsBean("bean1"));
assertNull(ctx.getBean(NonConfigurationClass.class)); }
@Test
public void nonConfigurationClassWithAsm() throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.registerBeanDefinition("config", new RootBeanDefinition(NonConfigurationClass.class.getName()));
ctx.refresh();
assertFalse(ctx.containsBean("bean1"));
} }
@Test @Test
@ -108,8 +118,15 @@ public class ConfigurationClassWithConditionTests {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(ConditionOnMethodConfiguration.class); ctx.register(ConditionOnMethodConfiguration.class);
ctx.refresh(); ctx.refresh();
thrown.expect(NoSuchBeanDefinitionException.class); assertFalse(ctx.containsBean("bean1"));
assertNull(ctx.getBean(ExampleBean.class)); }
@Test
public void methodConditionalWithAsm() throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.registerBeanDefinition("config", new RootBeanDefinition(ConditionOnMethodConfiguration.class.getName()));
ctx.refresh();
assertFalse(ctx.containsBean("bean1"));
} }
@Test @Test
@ -144,6 +161,7 @@ public class ConfigurationClassWithConditionTests {
@Configuration @Configuration
static class BeanOneConfiguration { static class BeanOneConfiguration {
@Bean @Bean
public ExampleBean bean1() { public ExampleBean bean1() {
return new ExampleBean(); return new ExampleBean();
@ -153,6 +171,7 @@ public class ConfigurationClassWithConditionTests {
@Configuration @Configuration
@Conditional(NoBeanOneCondition.class) @Conditional(NoBeanOneCondition.class)
static class BeanTwoConfiguration { static class BeanTwoConfiguration {
@Bean @Bean
public ExampleBean bean2() { public ExampleBean bean2() {
return new ExampleBean(); return new ExampleBean();
@ -162,6 +181,7 @@ public class ConfigurationClassWithConditionTests {
@Configuration @Configuration
@Conditional(HasBeanOneCondition.class) @Conditional(HasBeanOneCondition.class)
static class BeanThreeConfiguration { static class BeanThreeConfiguration {
@Bean @Bean
public ExampleBean bean3() { public ExampleBean bean3() {
return new ExampleBean(); return new ExampleBean();
@ -171,6 +191,7 @@ public class ConfigurationClassWithConditionTests {
@Configuration @Configuration
@MetaConditional("test") @MetaConditional("test")
static class ConfigurationWithMetaCondition { static class ConfigurationWithMetaCondition {
@Bean @Bean
public ExampleBean bean() { public ExampleBean bean() {
return new ExampleBean(); return new ExampleBean();
@ -180,14 +201,22 @@ public class ConfigurationClassWithConditionTests {
@Conditional(MetaConditionalFilter.class) @Conditional(MetaConditionalFilter.class)
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
public static @interface MetaConditional { public @interface MetaConditional {
String value(); String value();
} }
@Conditional(NeverCondition.class) @Conditional(NeverCondition.class)
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD}) @Target({ElementType.TYPE, ElementType.METHOD})
public static @interface Never { public @interface Never {
}
@Conditional(AlwaysCondition.class)
@Never
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface MetaNever {
} }
static class NoBeanOneCondition implements Condition { static class NoBeanOneCondition implements Condition {
@ -238,8 +267,13 @@ public class ConfigurationClassWithConditionTests {
} }
@Component @Component
@Never @MetaNever
static class NonConfigurationClass { static class NonConfigurationClass {
@Bean
public ExampleBean bean1() {
return new ExampleBean();
}
} }
@Configuration @Configuration
@ -254,7 +288,7 @@ public class ConfigurationClassWithConditionTests {
@Configuration @Configuration
@Never @Never
@Import({ ConfigurationNotCreated.class, RegistrarNotCreated.class, ImportSelectorNotCreated.class }) @Import({ConfigurationNotCreated.class, RegistrarNotCreated.class, ImportSelectorNotCreated.class})
static class ImportsNotCreated { static class ImportsNotCreated {
static { static {
if (true) throw new RuntimeException(); if (true) throw new RuntimeException();

2
spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java

@ -900,7 +900,7 @@ public class AnnotatedElementUtils {
try { try {
return searchWithGetSemantics(element, annotationType, annotationName, containerType, processor, return searchWithGetSemantics(element, annotationType, annotationName, containerType, processor,
new HashSet<AnnotatedElement>(), 0); new HashSet<AnnotatedElement>(), 0);
} }
catch (Throwable ex) { catch (Throwable ex) {
AnnotationUtils.rethrowAnnotationConfigurationException(ex); AnnotationUtils.rethrowAnnotationConfigurationException(ex);

2
spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java

@ -1749,7 +1749,7 @@ public abstract class AnnotationUtils {
} }
/** /**
* <p>If the supplied throwable is an {@link AnnotationConfigurationException}, * If the supplied throwable is an {@link AnnotationConfigurationException},
* it will be cast to an {@code AnnotationConfigurationException} and thrown, * it will be cast to an {@code AnnotationConfigurationException} and thrown,
* allowing it to propagate to the caller. * allowing it to propagate to the caller.
* <p>Otherwise, this method does nothing. * <p>Otherwise, this method does nothing.

33
spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2014 the original author or authors. * Copyright 2002-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -72,32 +72,43 @@ final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttrib
else { else {
attributes.add(0, this.attributes); attributes.add(0, this.attributes);
} }
Set<String> metaAnnotationTypeNames = new LinkedHashSet<String>(); Set<Annotation> visited = new LinkedHashSet<Annotation>();
Annotation[] metaAnnotations = AnnotationUtils.getAnnotations(annotationClass); Annotation[] metaAnnotations = AnnotationUtils.getAnnotations(annotationClass);
if (!ObjectUtils.isEmpty(metaAnnotations)) { if (!ObjectUtils.isEmpty(metaAnnotations)) {
for (Annotation metaAnnotation : metaAnnotations) { for (Annotation metaAnnotation : metaAnnotations) {
if (!AnnotationUtils.isInJavaLangAnnotationPackage(metaAnnotation)) { if (!AnnotationUtils.isInJavaLangAnnotationPackage(metaAnnotation)) {
recursivelyCollectMetaAnnotations(metaAnnotationTypeNames, metaAnnotation); recursivelyCollectMetaAnnotations(visited, metaAnnotation);
} }
} }
} }
if (this.metaAnnotationMap != null) { if (this.metaAnnotationMap != null) {
Set<String> metaAnnotationTypeNames = new LinkedHashSet<String>(visited.size());
for (Annotation ann : visited) {
metaAnnotationTypeNames.add(ann.annotationType().getName());
}
this.metaAnnotationMap.put(annotationClass.getName(), metaAnnotationTypeNames); this.metaAnnotationMap.put(annotationClass.getName(), metaAnnotationTypeNames);
} }
} }
private void recursivelyCollectMetaAnnotations(Set<String> visited, Annotation annotation) { private void recursivelyCollectMetaAnnotations(Set<Annotation> visited, Annotation annotation) {
String annotationName = annotation.annotationType().getName(); if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation) && visited.add(annotation)) {
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation) && visited.add(annotationName)) { try {
// Only do further scanning for public annotations; we'd run into // Only do attribute scanning for public annotations; we'd run into
// IllegalAccessExceptions otherwise, and we don't want to mess with // IllegalAccessExceptions otherwise, and we don't want to mess with
// accessibility in a SecurityManager environment. // accessibility in a SecurityManager environment.
if (Modifier.isPublic(annotation.annotationType().getModifiers())) { if (Modifier.isPublic(annotation.annotationType().getModifiers())) {
this.attributesMap.add(annotationName, AnnotationUtils.getAnnotationAttributes(annotation, false, true)); String annotationName = annotation.annotationType().getName();
this.attributesMap.add(annotationName, AnnotationUtils.getAnnotationAttributes(annotation, false, true));
}
for (Annotation metaMetaAnnotation : annotation.annotationType().getAnnotations()) { for (Annotation metaMetaAnnotation : annotation.annotationType().getAnnotations()) {
recursivelyCollectMetaAnnotations(visited, metaMetaAnnotation); recursivelyCollectMetaAnnotations(visited, metaMetaAnnotation);
} }
} }
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to introspect meta-annotations on [" + annotation + "]: " + ex);
}
}
} }
} }

7
spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationReadingVisitorUtils.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2015 the original author or authors. * Copyright 2002-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -150,9 +150,8 @@ abstract class AnnotationReadingVisitorUtils {
for (String overridableAttributeName : overridableAttributeNames) { for (String overridableAttributeName : overridableAttributeNames) {
Object value = currentAttributes.get(overridableAttributeName); Object value = currentAttributes.get(overridableAttributeName);
if (value != null) { if (value != null) {
// Store the value, potentially overriding a value from an // Store the value, potentially overriding a value from an attribute
// attribute of the same name found higher in the annotation // of the same name found higher in the annotation hierarchy.
// hierarchy.
results.put(overridableAttributeName, value); results.put(overridableAttributeName, value);
} }
} }

4
spring-core/src/main/java/org/springframework/core/type/classreading/RecursiveAnnotationAttributesVisitor.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2014 the original author or authors. * Copyright 2002-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -58,7 +58,7 @@ class RecursiveAnnotationAttributesVisitor extends AbstractRecursiveAnnotationVi
} }
private void registerDefaultValues(Class<?> annotationClass) { private void registerDefaultValues(Class<?> annotationClass) {
// Only do further scanning for public annotations; we'd run into // Only do defaults scanning for public annotations; we'd run into
// IllegalAccessExceptions otherwise, and we don't want to mess with // IllegalAccessExceptions otherwise, and we don't want to mess with
// accessibility in a SecurityManager environment. // accessibility in a SecurityManager environment.
if (Modifier.isPublic(annotationClass.getModifiers())) { if (Modifier.isPublic(annotationClass.getModifiers())) {

Loading…
Cancel
Save