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:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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())) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user