Update @Conditional processing logic

Defer @Conditional processing on @Configuration classes until the bean
definitions are loaded, rather than when the @Configuration class is
parsed. This provides better support for @Conditional implementations
that inspect bean definitions.

This commit also fixes some minor problems with original implementation
and replaces the ConditionalAnnotationHelper class with
ConditionEvaluator.

Issue: SPR-10534
This commit is contained in:
Phillip Webb
2013-05-30 15:35:18 -07:00
parent 2ecc51f066
commit 239ce1466c
13 changed files with 372 additions and 277 deletions

View File

@@ -92,7 +92,7 @@ public class AnnotatedBeanDefinitionReader {
/**
* Set the Environment to use when evaluating whether
* {@link Profile @Profile}-annotated component classes should be registered.
* {@link Conditional @Conditional}-annotated component classes should be registered.
* <p>The default is a {@link StandardEnvironment}.
* @see #registerBean(Class, String, Class...)
*/
@@ -133,8 +133,7 @@ public class AnnotatedBeanDefinitionReader {
public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
if (ConditionalAnnotationHelper.shouldSkip(abd, this.registry,
this.environment, this.beanNameGenerator)) {
if (ConditionEvaluator.get(abd.getMetadata(), true).shouldSkip(this.registry, this.environment)) {
return;
}
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);

View File

@@ -31,6 +31,7 @@ import org.springframework.core.env.Environment;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.util.Assert;
import org.springframework.util.PatternMatchUtils;
@@ -52,7 +53,6 @@ import org.springframework.util.PatternMatchUtils;
* @author Mark Fisher
* @author Juergen Hoeller
* @author Chris Beams
* @author Phillip Webb
* @since 2.5
* @see AnnotationConfigApplicationContext#scan
* @see org.springframework.stereotype.Component
@@ -262,6 +262,12 @@ 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.
@@ -299,10 +305,6 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
* bean definition has been found for the specified name
*/
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
if (ConditionalAnnotationHelper.shouldSkip(beanDefinition, getRegistry(),
getEnvironment(), this.beanNameGenerator)) {
return false;
}
if (!this.registry.containsBeanDefinition(beanName)) {
return true;
}

View File

@@ -25,12 +25,10 @@ import java.util.Set;
import org.apache.commons.logging.Log;
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.context.ResourceLoaderAware;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.env.StandardEnvironment;
@@ -39,7 +37,6 @@ import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
@@ -159,7 +156,7 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
/**
* Set the Environment to use when resolving placeholders and evaluating
* {@link Profile @Profile}-annotated component classes.
* {@link Conditional @Conditional}-annotated component classes.
* <p>The default is a {@link StandardEnvironment}
* @param environment the Environment to use
*/
@@ -333,17 +330,23 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
}
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, this.metadataReaderFactory)) {
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
if (!metadata.isAnnotated(Profile.class.getName())) {
return true;
}
AnnotationAttributes profile = MetadataUtils.attributesFor(metadata, Profile.class);
return this.environment.acceptsProfiles(profile.getStringArray("value"));
return isConditionMatch(metadataReader);
}
}
return false;
}
/**
* Determine whether the given class is a candidate component based on any
* {@code @Conditional} annotations.
* @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());
}
/**
* Determine whether the given bean definition qualifies as candidate.
* <p>The default implementation checks whether the class is concrete

View File

@@ -31,8 +31,8 @@ public interface ConditionContext {
/**
* Returns the {@link BeanDefinitionRegistry} that will hold the bean definition
* should the condition match.
* @return the registry (never {@code null})
* should the condition match or {@code null} if the registry is not available.
* @return the registry or {@code null}
*/
BeanDefinitionRegistry getRegistry();
@@ -45,13 +45,11 @@ public interface ConditionContext {
/**
* Returns the {@link ConfigurableListableBeanFactory} that will hold the bean
* definition should the condition match. If a
* {@link ConfigurableListableBeanFactory} is unavailable an
* {@link IllegalStateException} will be thrown.
* @return the bean factory
* @throws IllegalStateException if the bean factory could not be obtained
* definition should the condition match or {@code null} if the bean factory is
* not available.
* @return the bean factory or {@code null}
*/
ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
ConfigurableListableBeanFactory getBeanFactory();
/**
* Returns the {@link ResourceLoader} currently being used or {@code null} if the

View File

@@ -0,0 +1,213 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context.annotation;
import java.util.Collections;
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.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.MultiValueMap;
/**
* Utility class used to evaluate {@link Conditional} annotations.
*
* @author Phillip Webb
* @since 4.0
*/
abstract 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;
}
};
/**
* 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
*/
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);
}
/**
* Implementation of {@link ConditionEvaluator}.
*/
private static class ConditionEvaluatorImpl extends ConditionEvaluator {
private AnnotatedTypeMetadata metadata;
public ConditionEvaluatorImpl(AnnotatedTypeMetadata metadata) {
this.metadata = metadata;
}
@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;
}
}
}
}
return false;
}
@SuppressWarnings("unchecked")
private static List<String[]> getConditionClasses(AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(
CONDITIONAL_ANNOTATION, true);
Object values = attributes == null ? null : attributes.get("value");
return (List<String[]>) (values == null ? Collections.emptyList() : values);
}
private static Condition getCondition(String conditionClassName,
ClassLoader classloader) {
Class<?> conditionClass = ClassUtils.resolveClassName(conditionClassName,
classloader);
return (Condition) BeanUtils.instantiateClass(conditionClass);
}
}
/**
* Implementation of a {@link ConditionContext}.
*/
private static class ConditionContextImpl implements ConditionContext {
private BeanDefinitionRegistry registry;
private ConfigurableListableBeanFactory beanFactory;
private Environment environment;
public ConditionContextImpl(BeanDefinitionRegistry registry,
Environment environment) {
this.registry = registry;
this.beanFactory = deduceBeanFactory(registry);
this.environment = environment;
if (this.environment == null) {
this.environment = deduceEnvironment(registry);
}
}
private ConfigurableListableBeanFactory deduceBeanFactory(Object source) {
if (source == null) {
return null;
}
if (source instanceof ConfigurableListableBeanFactory) {
return (ConfigurableListableBeanFactory) source;
}
else if (source instanceof ConfigurableApplicationContext) {
return deduceBeanFactory(((ConfigurableApplicationContext) source).getBeanFactory());
}
return null;
}
private Environment deduceEnvironment(BeanDefinitionRegistry registry) {
if (registry == null) {
return null;
}
if (registry instanceof EnvironmentCapable) {
return ((EnvironmentCapable) registry).getEnvironment();
}
return null;
}
@Override
public BeanDefinitionRegistry getRegistry() {
return this.registry;
}
@Override
public Environment getEnvironment() {
return this.environment;
}
@Override
public ConfigurableListableBeanFactory getBeanFactory() {
Assert.state(this.beanFactory != null, "Unable to locate the BeanFactory");
return this.beanFactory;
}
@Override
public ResourceLoader getResourceLoader() {
if (registry instanceof ResourceLoader) {
return (ResourceLoader) registry;
}
return null;
}
@Override
public ClassLoader getClassLoader() {
ResourceLoader resourceLoader = getResourceLoader();
return (resourceLoader == null ? null : resourceLoader.getClassLoader());
}
}
}

View File

@@ -1,217 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context.annotation;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.MultiValueMap;
/**
* Helper class used to determine if registration should be skipped based due to a
* {@code @Conditional} annotation.
*
* @author Phillip Webb
* @since 4.0
* @see Conditional
*/
abstract class ConditionalAnnotationHelper {
private static final String CONDITIONAL_ANNOTATION = Conditional.class.getName();
public static boolean shouldSkip(BeanDefinition beanDefinition,
BeanDefinitionRegistry registry, Environment environment,
BeanNameGenerator beanNameGenerator) {
if (hasCondition(getMetadata(beanDefinition))) {
ConditionContextImpl context = new ConditionContextImpl(registry,
environment, beanNameGenerator);
return shouldSkip(getMetadata(beanDefinition), context);
}
return false;
}
public static boolean shouldSkip(BeanMethod beanMethod,
BeanDefinitionRegistry registry, Environment environment,
BeanNameGenerator beanNameGenerator) {
if (hasCondition(getMetadata(beanMethod))) {
ConditionContextImpl context = new ConditionContextImpl(registry,
environment, beanNameGenerator);
return shouldSkip(getMetadata(beanMethod), context);
}
return false;
}
public static boolean shouldSkip(ConfigurationClass configurationClass,
BeanDefinitionRegistry registry, Environment environment,
BeanNameGenerator beanNameGenerator) {
if (hasCondition(configurationClass)) {
ConditionContextImpl context = new ConditionContextImpl(registry,
environment, beanNameGenerator);
return shouldSkip(configurationClass, context);
}
return false;
}
public static boolean shouldSkip(ConfigurationClass configClass,
ConditionContextImpl context) {
if (configClass == null) {
return false;
}
return shouldSkip(configClass.getMetadata(), context);
}
private static boolean shouldSkip(AnnotatedTypeMetadata metadata,
ConditionContextImpl context) {
if (metadata != null) {
for (String[] conditionClasses : getConditionClasses(metadata)) {
for (String conditionClass : conditionClasses) {
if (!getCondition(conditionClass, context.getClassLoader()).matches(
context, metadata)) {
return true;
}
}
}
}
return false;
}
private static AnnotatedTypeMetadata getMetadata(BeanMethod beanMethod) {
return (beanMethod == null ? null : beanMethod.getMetadata());
}
private static AnnotatedTypeMetadata getMetadata(BeanDefinition beanDefinition) {
if (beanDefinition != null && beanDefinition instanceof AnnotatedBeanDefinition) {
return ((AnnotatedBeanDefinition) beanDefinition).getMetadata();
}
return null;
}
private static boolean hasCondition(ConfigurationClass configurationClass) {
if (configurationClass == null) {
return false;
}
return hasCondition(configurationClass.getMetadata())
|| hasCondition(configurationClass.getImportedBy());
}
private static boolean hasCondition(AnnotatedTypeMetadata metadata) {
return (metadata != null) && metadata.isAnnotated(CONDITIONAL_ANNOTATION);
}
@SuppressWarnings("unchecked")
private static List<String[]> getConditionClasses(AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(
CONDITIONAL_ANNOTATION, true);
Object values = attributes == null ? null : attributes.get("value");
return (List<String[]>) (values == null ? Collections.emptyList() : values);
}
private static Condition getCondition(String conditionClassName,
ClassLoader classloader) {
Class<?> conditionClass = ClassUtils.resolveClassName(conditionClassName,
classloader);
return (Condition) BeanUtils.instantiateClass(conditionClass);
}
/**
* Implementation of a {@link ConditionContext}.
*/
private static class ConditionContextImpl implements ConditionContext {
private BeanDefinitionRegistry registry;
private ConfigurableListableBeanFactory beanFactory;
private Environment environment;
public ConditionContextImpl(BeanDefinitionRegistry registry,
Environment environment, BeanNameGenerator beanNameGenerator) {
Assert.notNull(registry, "Registry must not be null");
this.registry = registry;
this.beanFactory = deduceBeanFactory(registry);
this.environment = environment;
if (this.environment == null) {
this.environment = deduceEnvironment(registry);
}
}
private ConfigurableListableBeanFactory deduceBeanFactory(Object source) {
if (source instanceof ConfigurableListableBeanFactory) {
return (ConfigurableListableBeanFactory) source;
}
else if (source instanceof ConfigurableApplicationContext) {
return deduceBeanFactory(((ConfigurableApplicationContext) source).getBeanFactory());
}
return null;
}
private Environment deduceEnvironment(BeanDefinitionRegistry registry) {
if (registry instanceof EnvironmentCapable) {
return ((EnvironmentCapable) registry).getEnvironment();
}
return null;
}
@Override
public BeanDefinitionRegistry getRegistry() {
return this.registry;
}
@Override
public Environment getEnvironment() {
return this.environment;
}
@Override
public ConfigurableListableBeanFactory getBeanFactory() {
Assert.state(this.beanFactory != null, "Unable to locate the BeanFactory");
return this.beanFactory;
}
@Override
public ResourceLoader getResourceLoader() {
if (registry instanceof ResourceLoader) {
return (ResourceLoader) registry;
}
return null;
}
@Override
public ClassLoader getClassLoader() {
ResourceLoader resourceLoader = getResourceLoader();
return (resourceLoader == null ? null : resourceLoader.getClassLoader());
}
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.context.annotation;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
@@ -61,6 +62,9 @@ final class ConfigurationClass {
private final Map<String, Class<? extends BeanDefinitionReader>> importedResources =
new LinkedHashMap<String, Class<? extends BeanDefinitionReader>>();
private final Set<ImportBeanDefinitionRegistrar> importBeanDefinitionRegistrars =
new LinkedHashSet<ImportBeanDefinitionRegistrar>();
/**
* Create a new {@link ConfigurationClass} with the given name.
@@ -173,11 +177,18 @@ final class ConfigurationClass {
this.importedResources.put(importedResource, readerClass);
}
public void addImportBeanDefinitionRegistrar(ImportBeanDefinitionRegistrar registrar) {
this.importBeanDefinitionRegistrars.add(registrar);
}
public Set<ImportBeanDefinitionRegistrar> getImportBeanDefinitionRegistrars() {
return Collections.unmodifiableSet(importBeanDefinitionRegistrars);
}
public Map<String, Class<? extends BeanDefinitionReader>> getImportedResources() {
return this.importedResources;
}
public void validate(ProblemReporter problemReporter) {
// A configuration class may not be final (CGLIB limitation)
if (getMetadata().isAnnotated(Configuration.class.getName())) {

View File

@@ -16,8 +16,6 @@
package org.springframework.context.annotation;
import static org.springframework.context.annotation.MetadataUtils.attributesFor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
@@ -28,6 +26,7 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
import org.springframework.beans.factory.annotation.Autowire;
@@ -52,6 +51,8 @@ import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.StringUtils;
import static org.springframework.context.annotation.MetadataUtils.*;
/**
* Reads a given fully-populated set of ConfigurationClass instances, registering bean
* definitions with the given {@link BeanDefinitionRegistry} based on its contents.
@@ -84,7 +85,6 @@ class ConfigurationClassBeanDefinitionReader {
private final BeanNameGenerator importBeanNameGenerator;
/**
* Create a new {@link ConfigurationClassBeanDefinitionReader} instance that will be used
* to populate the given {@link BeanDefinitionRegistry}.
@@ -109,8 +109,9 @@ class ConfigurationClassBeanDefinitionReader {
* based on its contents.
*/
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator conditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass);
loadBeanDefinitionsForConfigurationClass(configClass, conditionEvaluator);
}
}
@@ -118,7 +119,14 @@ class ConfigurationClassBeanDefinitionReader {
* Read a particular {@link ConfigurationClass}, registering bean definitions for the
* class itself, all its {@link Bean} methods
*/
public void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) {
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,
TrackedConditionEvaluator conditionEvaluator) {
if(conditionEvaluator.shouldSkip(configClass)) {
removeBeanDefinition(configClass);
return;
}
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
@@ -126,6 +134,17 @@ class ConfigurationClassBeanDefinitionReader {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
loadBeanDefinitionsFromRegistrars(configClass.getMetadata(), configClass.getImportBeanDefinitionRegistrars());
}
private void removeBeanDefinition(ConfigurationClass configClass) {
if (StringUtils.hasLength(configClass.getBeanName())) {
try {
this.registry.removeBeanDefinition(configClass.getBeanName());
}
catch (NoSuchBeanDefinitionException ex) {
}
}
}
/**
@@ -153,8 +172,8 @@ class ConfigurationClassBeanDefinitionReader {
* with the BeanDefinitionRegistry based on its contents.
*/
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
if (ConditionalAnnotationHelper.shouldSkip(beanMethod, this.registry,
this.environment, this.importBeanNameGenerator)) {
if (ConditionEvaluator.get(beanMethod.getMetadata(), false).shouldSkip(
this.registry, this.environment)) {
return;
}
ConfigurationClass configClass = beanMethod.getConfigurationClass();
@@ -300,6 +319,14 @@ class ConfigurationClassBeanDefinitionReader {
}
}
private void loadBeanDefinitionsFromRegistrars(
AnnotationMetadata importingClassMetadata,
Set<ImportBeanDefinitionRegistrar> importBeanDefinitionRegistrars) {
for (ImportBeanDefinitionRegistrar registrar : importBeanDefinitionRegistrars) {
registrar.registerBeanDefinitions(importingClassMetadata, this.registry);
}
}
/**
* {@link RootBeanDefinition} marker subclass used to signify that a bean definition
@@ -357,4 +384,32 @@ class ConfigurationClassBeanDefinitionReader {
}
}
/**
* Evaluate {@Code @Conditional} annotations, tracking results and taking into
* account 'imported by'.
*/
private class TrackedConditionEvaluator {
private final Map<ConfigurationClass, Boolean> skipped = new HashMap<ConfigurationClass, Boolean>();
public boolean shouldSkip(ConfigurationClass configClass) {
Boolean skip = this.skipped.get(configClass);
if (skip == null) {
if (configClass.isImported()) {
if (shouldSkip(configClass.getImportedBy())) {
// The config that imported this one was skipped, therefore we are skipped
skip = true;
}
}
if (skip == null) {
skip = ConditionEvaluator.get(configClass.getMetadata(), false).shouldSkip(
registry, environment);
}
this.skipped.put(configClass, skip);
}
return skip;
}
}
}

View File

@@ -76,7 +76,8 @@ import static org.springframework.context.annotation.MetadataUtils.*;
*
* <p>This class helps separate the concern of parsing the structure of a Configuration
* class from the concern of registering BeanDefinition objects based on the
* content of that model.
* content of that model (with the exception of {@code @ComponentScan} annotations which
* need to be registered immediately).
*
* <p>This ASM-based implementation avoids reflection and eager class loading in order to
* interoperate effectively with lazy class loading in a Spring ApplicationContext.
@@ -108,8 +109,6 @@ class ConfigurationClassParser {
private final BeanDefinitionRegistry registry;
private final BeanNameGenerator beanNameGenerator;
private final ComponentScanAnnotationParser componentScanParser;
private final Set<ConfigurationClass> configurationClasses = new LinkedHashSet<ConfigurationClass>();
@@ -136,7 +135,6 @@ class ConfigurationClassParser {
this.environment = environment;
this.resourceLoader = resourceLoader;
this.registry = registry;
this.beanNameGenerator = componentScanBeanNameGenerator;
this.componentScanParser = new ComponentScanAnnotationParser(
resourceLoader, environment, componentScanBeanNameGenerator, registry);
}
@@ -182,9 +180,6 @@ class ConfigurationClassParser {
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
if (ConditionalAnnotationHelper.shouldSkip(configClass, this.registry, this.environment, this.beanNameGenerator)) {
return;
}
if (this.configurationClasses.contains(configClass) && configClass.getBeanName() != null) {
// Explicit bean definition found, probably replacing an import.
@@ -226,13 +221,16 @@ class ConfigurationClassParser {
AnnotationAttributes componentScan = attributesFor(metadata, ComponentScan.class);
if (componentScan != null) {
// the config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, metadata.getClassName());
if (!ConditionEvaluator.get(configClass.getMetadata(), false).shouldSkip(
this.registry, this.environment)) {
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, metadata.getClassName());
// check the set of scanned definitions for any further config classes and parse recursively if necessary
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) {
this.parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
// check the set of scanned definitions for any further config classes and parse recursively if necessary
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) {
this.parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
}
}
}
}
@@ -451,7 +449,7 @@ class ConfigurationClassParser {
this.resourceLoader.getClassLoader().loadClass((String) candidate));
ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
invokeAwareMethods(registrar);
registrar.registerBeanDefinitions(importingClassMetadata, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar);
}
else {
// candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> process it as a @Configuration class

View File

@@ -26,7 +26,6 @@ import java.util.Stack;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanDefinitionStoreException;
@@ -305,12 +304,8 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
registry, this.sourceExtractor, this.problemReporter, this.metadataReaderFactory,
this.resourceLoader, this.environment, this.importBeanNameGenerator);
}
for (ConfigurationClass configurationClass : parser.getConfigurationClasses()) {
if (!ConditionalAnnotationHelper.shouldSkip(configurationClass, registry,
this.environment, this.importBeanNameGenerator)) {
reader.loadBeanDefinitionsForConfigurationClass(configurationClass);
}
}
reader.loadBeanDefinitions(parser.getConfigurationClasses());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
if (singletonRegistry != null) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.