diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java b/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java index a19488d93c..be17b59d6d 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java @@ -45,12 +45,12 @@ public class AnnotatedBeanDefinitionReader { private final BeanDefinitionRegistry registry; - private Environment environment; - private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator(); private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver(); + private ConditionEvaluator conditionEvaluator; + /** * Create a new {@code AnnotatedBeanDefinitionReader} for the given registry. @@ -79,7 +79,8 @@ public class AnnotatedBeanDefinitionReader { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); Assert.notNull(environment, "Environment must not be null"); this.registry = registry; - this.environment = environment; + this.conditionEvaluator = new ConditionEvaluator(registry, environment, + null, null, null); AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); } @@ -97,7 +98,8 @@ public class AnnotatedBeanDefinitionReader { * @see #registerBean(Class, String, Class...) */ public void setEnvironment(Environment environment) { - this.environment = environment; + this.conditionEvaluator = new ConditionEvaluator(this.registry, environment, + null, null, null); } /** @@ -133,7 +135,7 @@ public class AnnotatedBeanDefinitionReader { public void registerBean(Class annotatedClass, String name, Class... qualifiers) { AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass); - if (ConditionEvaluator.get(abd.getMetadata(), true).shouldSkip(this.registry, this.environment)) { + if (conditionEvaluator.shouldSkip(abd.getMetadata())) { return; } ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd); diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java index b673b40fc4..42f5b0ab9a 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java @@ -262,12 +262,6 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo return beanDefinitions; } - @Override - protected boolean isConditionMatch(MetadataReader metadataReader) { - return !ConditionEvaluator.get(metadataReader.getAnnotationMetadata(), true).shouldSkip( - getRegistry(), getEnvironment()); - } - /** * Apply further settings to the given bean definition, * beyond the contents retrieved from scanning the component class. diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java index 5c66d8f211..08336b34b0 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java @@ -28,6 +28,7 @@ import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.ResourceLoaderAware; import org.springframework.core.env.Environment; import org.springframework.core.env.EnvironmentCapable; @@ -85,6 +86,8 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC private final List excludeFilters = new LinkedList(); + private ConditionEvaluator conditionEvaluator; + /** * Create a ClassPathScanningCandidateComponentProvider with a {@link StandardEnvironment}. @@ -162,6 +165,7 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC */ public void setEnvironment(Environment environment) { this.environment = environment; + this.conditionEvaluator = null; } @Override @@ -169,6 +173,13 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC return this.environment; } + /** + * Returns the {@link BeanDefinitionRegistry} used by this scanner or {@code null}. + */ + protected BeanDefinitionRegistry getRegistry() { + return null; + } + /** * Set the resource pattern to use when scanning the classpath. * This value will be appended to each base package name. @@ -342,9 +353,12 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC * @param metadataReader the ASM ClassReader for the class * @return whether the class qualifies as a candidate component */ - protected boolean isConditionMatch(MetadataReader metadataReader) { - return !ConditionEvaluator.get(metadataReader.getAnnotationMetadata(), true).shouldSkip( - null, getEnvironment()); + private boolean isConditionMatch(MetadataReader metadataReader) { + if (this.conditionEvaluator == null) { + this.conditionEvaluator = new ConditionEvaluator(getRegistry(), + getEnvironment(), null, null, getResourceLoader()); + } + return !conditionEvaluator.shouldSkip(metadataReader.getAnnotationMetadata()); } /** diff --git a/spring-context/src/main/java/org/springframework/context/annotation/Condition.java b/spring-context/src/main/java/org/springframework/context/annotation/Condition.java index 69543830aa..a3cfe9fc80 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/Condition.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/Condition.java @@ -25,15 +25,18 @@ import org.springframework.core.type.AnnotationMetadata; * A single {@code condition} that must be {@linkplain #matches matched} in order * for a component to be registered. * - *

Conditions are checked immediately before a component bean-definition is due to be - * registered and are free to veto registration based on any criteria that can be - * determined at that point. + *

Conditions are checked immediately before the bean-definition is due to be + * registered and are free to veto registration based on any criteria that can + * be determined at that point. * *

Conditions must follow the same restrictions as {@link BeanFactoryPostProcessor} - * and take care to never interact with bean instances. + * and take care to never interact with bean instances. For more fine-grained control + * of conditions that interact with {@code @Configuration} beans consider the + * {@link ConfigurationCondition} interface. * * @author Phillip Webb * @since 4.0 + * @see ConfigurationCondition * @see Conditional * @see ConditionContext */ diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConditionContext.java b/spring-context/src/main/java/org/springframework/context/annotation/ConditionContext.java index bca2fc8f1b..541f749c25 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConditionContext.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConditionContext.java @@ -18,6 +18,7 @@ package org.springframework.context.annotation; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.ApplicationContext; import org.springframework.core.env.Environment; import org.springframework.core.io.ResourceLoader; @@ -65,4 +66,6 @@ public interface ConditionContext { */ ClassLoader getClassLoader(); + ApplicationContext getApplicationContext(); + } diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConditionEvaluator.java b/spring-context/src/main/java/org/springframework/context/annotation/ConditionEvaluator.java index 2f7342f8dd..32142bc59e 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConditionEvaluator.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConditionEvaluator.java @@ -22,7 +22,9 @@ import java.util.List; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase; import org.springframework.core.env.Environment; import org.springframework.core.env.EnvironmentCapable; import org.springframework.core.io.ResourceLoader; @@ -33,106 +35,93 @@ import org.springframework.util.ClassUtils; import org.springframework.util.MultiValueMap; /** - * Utility class used to evaluate {@link Conditional} annotations. + * Internal class used to evaluate {@link Conditional} annotations. * * @author Phillip Webb * @since 4.0 */ -abstract class ConditionEvaluator { +class ConditionEvaluator { private static final String CONDITIONAL_ANNOTATION = Conditional.class.getName(); - private static final ConditionEvaluator NONE = new ConditionEvaluator() { - @Override - public boolean shouldSkip(BeanDefinitionRegistry registry, Environment environment) { - return false; - } - - }; + private final ConditionContextImpl context; /** - * Evaluate if any condition does not match and hence registration should be skipped. - * @param registry the registry or {@code null} - * @param environment the environment or {@code null} - * @return if the registration should be skipped + * Create a new {@link ConditionEvaluator} instance. */ - public abstract boolean shouldSkip(BeanDefinitionRegistry registry, - Environment environment); - - - /** - * Returns a {@link ConditionEvaluator} instance of the specified metadata. - * @param metadata the metadata to test - * @param deferIfConfigurationCandidate if the evaluator should be deferred when the - * metadata is from a {@code @Configuration} candidate. - * @return the evaluator instance - */ - public static ConditionEvaluator get(AnnotatedTypeMetadata metadata, - boolean deferIfConfigurationCandidate) { - if (metadata == null || !metadata.isAnnotated(CONDITIONAL_ANNOTATION)) { - // Shortcut to save always creating a ConditionEvaluator - return NONE; - } - - // Defer @Conditional @Configuration classes until later when the - // ConfigurationClassPostProcessor will evaluate them. Allows @Conditional - // implementations that inspect beans created by @Configuration to work - if (deferIfConfigurationCandidate && metadata instanceof AnnotationMetadata - && ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) { - return NONE; - } - - return new ConditionEvaluatorImpl(metadata); + public ConditionEvaluator(BeanDefinitionRegistry registry, Environment environment, + ApplicationContext applicationContext, ClassLoader classLoader, + ResourceLoader resourceLoader) { + this.context = new ConditionContextImpl(registry, environment, + applicationContext, classLoader, resourceLoader); } /** - * Implementation of {@link ConditionEvaluator}. + * Determine if an item should be skipped based on {@code @Conditional} annotations. + * The {@link ConfigurationPhase} will be deduced from the type of item (i.e. a + * {@code @Configuration} class will be {@link ConfigurationPhase#PARSE_CONFIGURATION}) + * @param metadata the meta data + * @return if the item should be skipped */ - private static class ConditionEvaluatorImpl extends ConditionEvaluator { + public boolean shouldSkip(AnnotatedTypeMetadata metadata) { + return shouldSkip(metadata, null); + } - private AnnotatedTypeMetadata metadata; - - - public ConditionEvaluatorImpl(AnnotatedTypeMetadata metadata) { - this.metadata = metadata; + /** + * Determine if an item should be skipped based on {@code @Conditional} annotations. + * @param metadata the meta data + * @param phase the phase of the call + * @return if the item should be skipped + */ + public boolean shouldSkip(AnnotatedTypeMetadata metadata, ConfigurationPhase phase) { + if (metadata == null || !metadata.isAnnotated(CONDITIONAL_ANNOTATION)) { + return false; } + if (phase == null) { + if (metadata instanceof AnnotationMetadata && + ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) { + return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION); + } + return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN); + } - @Override - public boolean shouldSkip(BeanDefinitionRegistry registry, Environment environment) { - ConditionContext context = new ConditionContextImpl(registry, environment); - if (this.metadata != null) { - for (String[] conditionClasses : getConditionClasses(metadata)) { - for (String conditionClass : conditionClasses) { - if (!getCondition(conditionClass, context.getClassLoader()).matches( - context, metadata)) { - return true; - } + for (String[] conditionClasses : getConditionClasses(metadata)) { + for (String conditionClass : conditionClasses) { + Condition condition = getCondition(conditionClass, context.getClassLoader()); + ConfigurationPhase requiredPhase = null; + if (condition instanceof ConfigurationCondition) { + requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase(); + } + if (requiredPhase == null || requiredPhase == phase) { + if (!condition.matches(context, metadata)) { + return true; } } } - return false; - } - - @SuppressWarnings("unchecked") - private static List getConditionClasses(AnnotatedTypeMetadata metadata) { - MultiValueMap attributes = metadata.getAllAnnotationAttributes( - CONDITIONAL_ANNOTATION, true); - Object values = attributes == null ? null : attributes.get("value"); - return (List) (values == null ? Collections.emptyList() : values); - } - - private static Condition getCondition(String conditionClassName, - ClassLoader classloader) { - Class conditionClass = ClassUtils.resolveClassName(conditionClassName, - classloader); - return (Condition) BeanUtils.instantiateClass(conditionClass); } + return false; } + @SuppressWarnings("unchecked") + private List getConditionClasses(AnnotatedTypeMetadata metadata) { + MultiValueMap attributes = metadata.getAllAnnotationAttributes( + CONDITIONAL_ANNOTATION, true); + Object values = attributes == null ? null : attributes.get("value"); + return (List) (values == null ? Collections.emptyList() : values); + } + + private Condition getCondition(String conditionClassName, + ClassLoader classloader) { + Class conditionClass = ClassUtils.resolveClassName(conditionClassName, + classloader); + return (Condition) BeanUtils.instantiateClass(conditionClass); + } + + /** * Implementation of a {@link ConditionContext}. */ @@ -144,18 +133,24 @@ abstract class ConditionEvaluator { private Environment environment; + private ApplicationContext applicationContext; + + private ClassLoader classLoader; + + private ResourceLoader resourceLoader; + public ConditionContextImpl(BeanDefinitionRegistry registry, - Environment environment) { + Environment environment, ApplicationContext applicationContext, + ClassLoader classLoader, ResourceLoader resourceLoader) { this.registry = registry; this.beanFactory = deduceBeanFactory(registry); this.environment = environment; - if (this.environment == null) { - this.environment = deduceEnvironment(registry); - } + this.applicationContext = applicationContext; + this.classLoader = classLoader; + this.resourceLoader = resourceLoader; } - private ConfigurableListableBeanFactory deduceBeanFactory(Object source) { if (source == null) { return null; @@ -169,24 +164,26 @@ abstract class ConditionEvaluator { return null; } - private Environment deduceEnvironment(BeanDefinitionRegistry registry) { - if (registry == null) { - return null; + @Override + public BeanDefinitionRegistry getRegistry() { + if (this.registry != null) { + return this.registry; } - if (registry instanceof EnvironmentCapable) { - return ((EnvironmentCapable) registry).getEnvironment(); + if(getBeanFactory() != null && getBeanFactory() instanceof BeanDefinitionRegistry) { + return (BeanDefinitionRegistry) getBeanFactory(); } return null; } - @Override - public BeanDefinitionRegistry getRegistry() { - return this.registry; - } - @Override public Environment getEnvironment() { - return this.environment; + if (this.environment != null) { + return this.environment; + } + if (getRegistry() != null && getRegistry() instanceof EnvironmentCapable) { + return ((EnvironmentCapable) getRegistry()).getEnvironment(); + } + return null; } @Override @@ -197,6 +194,9 @@ abstract class ConditionEvaluator { @Override public ResourceLoader getResourceLoader() { + if (this.resourceLoader != null) { + return this.resourceLoader; + } if (registry instanceof ResourceLoader) { return (ResourceLoader) registry; } @@ -205,8 +205,24 @@ abstract class ConditionEvaluator { @Override public ClassLoader getClassLoader() { - ResourceLoader resourceLoader = getResourceLoader(); - return (resourceLoader == null ? null : resourceLoader.getClassLoader()); + if (this.classLoader != null) { + return this.classLoader; + } + if (getResourceLoader() != null) { + return getResourceLoader().getClassLoader(); + } + return null; + } + + @Override + public ApplicationContext getApplicationContext() { + if (this.applicationContext != null) { + return this.applicationContext; + } + if (getRegistry() != null && getRegistry() instanceof ApplicationContext) { + return (ApplicationContext) getRegistry(); + } + return null; } } diff --git a/spring-context/src/main/java/org/springframework/context/annotation/Conditional.java b/spring-context/src/main/java/org/springframework/context/annotation/Conditional.java index 795ebfcc69..898e3ebcd3 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/Conditional.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/Conditional.java @@ -26,7 +26,7 @@ import java.lang.annotation.Target; * {@linkplain #value() specified conditions} match. * *

A condition is any state that can be determined programmatically - * immediately before the bean is due to be created (see {@link Condition} for details). + * before the bean definition is due to be registered (see {@link Condition} for details). * *

The {@code @Conditional} annotation may be used in any of the following ways: *