Support for @Conditional configuration

Introduce new @Conditional annotation that can be used to filter
which @Configuration classes or methods are loaded. @Conditional
can be used directly or as a meta-annotation. Condition implementations
are provided via the 'Condition' interface and are free to filter based
on any criteria available at the time that they run. The
ConditionalContext provides access to the BeanDefinitionRegistry,
Environment and ConfigurableListableBeanFactory along with a
ResourceLoader and ClassLoader.

The existing @Profile annotation has been refactored as a @Conditional
with the added benefit that it can now be used as a method level
annotation.
This commit is contained in:
Phillip Webb
2013-02-20 10:32:53 -08:00
parent 8e445f3a21
commit b257253a2b
13 changed files with 681 additions and 45 deletions

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.
@@ -24,11 +24,9 @@ import org.springframework.beans.factory.support.AutowireCandidateQualifier;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;
/**
@@ -39,6 +37,7 @@ import org.springframework.util.Assert;
* @author Juergen Hoeller
* @author Chris Beams
* @author Sam Brannen
* @author Phillip Webb
* @since 3.0
* @see AnnotationConfigApplicationContext#register
*/
@@ -134,12 +133,9 @@ public class AnnotatedBeanDefinitionReader {
public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
AnnotationMetadata metadata = abd.getMetadata();
if (metadata.isAnnotated(Profile.class.getName())) {
AnnotationAttributes profile = MetadataUtils.attributesFor(metadata, Profile.class);
if (!this.environment.acceptsProfiles(profile.getStringArray("value"))) {
return;
}
if (ConditionalAnnotationHelper.shouldSkip(abd, this.registry,
this.environment, this.beanNameGenerator)) {
return;
}
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());

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.
@@ -52,6 +52,7 @@ 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
@@ -298,6 +299,10 @@ 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

@@ -0,0 +1,52 @@
/*
* 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 org.jruby.internal.runtime.methods.MethodMethod;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.AnnotationMetadata;
/**
* A single {@code condition} that must be {@linkplain #matches matched} in order
* for a component to be registered.
*
* <p>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.
*
* <p>Conditions must follow the same restrictions as {@link BeanFactoryPostProcessor}
* and take care to never interact with bean instances.
*
* @author Phillip Webb
* @since 4.0
* @see Conditional
* @see ConditionContext
*/
public interface Condition {
/**
* Determine if the condition matches.
* @param context the condition context
* @param metadata meta-data of the {@link AnnotationMetadata class} or
* {@link MethodMethod method} being checked.
* @return {@code true} if the condition matches and the component can be registered
* or {@code false} to veto registration.
*/
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

View File

@@ -0,0 +1,70 @@
/*
* 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 org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
/**
* Context information for use by {@link Condition}s.
*
* @author Phillip Webb
* @since 4.0
*/
public interface ConditionContext {
/**
* Returns the {@link BeanDefinitionRegistry} that will hold the bean definition
* should the condition match.
* @return the registry (never {@code null})
*/
BeanDefinitionRegistry getRegistry();
/**
* Return the {@link Environment} for which the current application is running or
* {@code null} if no environment is available.
* @return the environment or {@code null}
*/
Environment getEnvironment();
/**
* 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
*/
ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
/**
* Returns the {@link ResourceLoader} currently being used or {@code null} if the
* resource loader cannot be obtained.
* @return a resource loader or {@code null}
*/
ResourceLoader getResourceLoader();
/**
* Returns the {@link ClassLoader} that should be used to load additional classes
* or {@code null} if the default classloader should be used.
* @return the classloader or {@code null}
*/
ClassLoader getClassLoader();
}

View File

@@ -0,0 +1,58 @@
/*
* 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.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Indicates that a component is is only eligible for registration when all
* {@linkplain #value() specified conditions} match.
*
* <p>A <em>condition</em> is any state that can be determined programmatically
* immediately before the bean is due to be created (see {@link Condition} for details).
*
* <p>The {@code @Conditional} annotation may be used in any of the following ways:
* <ul>
* <li>as a type-level annotation on any class directly or indirectly annotated with
* {@code @Component}, including {@link Configuration @Configuration} classes</li>
* <li>as a meta-annotation, for the purpose of composing custom stereotype
* annotations</li>
* <li>as a method-level annotation on any {@link Bean @Bean} method</li>
* </ul>
*
* <p>If a {@code @Configuration} class is marked with {@code @Conditional}, all of the
* {@code @Bean} methods and {@link Import @Import} annotations associated with that class
* will be subject to the conditions.
*
* @author Phillip Webb
* @since 4.0
* @see Condition
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
public @interface Conditional {
/**
* All {@link Condition}s that must {@linkplain Condition#matches match} in order for
* the component to be registered.
*/
Class<? extends Condition>[] value();
}

View File

@@ -0,0 +1,212 @@
/*
* 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;
}
public BeanDefinitionRegistry getRegistry() {
return this.registry;
}
public Environment getEnvironment() {
return this.environment;
}
public ConfigurableListableBeanFactory getBeanFactory() {
Assert.state(this.beanFactory != null, "Unable to locate the BeanFactory");
return this.beanFactory;
}
public ResourceLoader getResourceLoader() {
if (registry instanceof ResourceLoader) {
return (ResourceLoader) registry;
}
return null;
}
public ClassLoader getClassLoader() {
ResourceLoader resourceLoader = getResourceLoader();
return (resourceLoader == null ? null : resourceLoader.getClassLoader());
}
}
}

View File

@@ -41,6 +41,7 @@ import org.springframework.util.ClassUtils;
*
* @author Chris Beams
* @author Juergen Hoeller
* @author Phillip Webb
* @since 3.0
* @see BeanMethod
* @see ConfigurationClassParser
@@ -58,7 +59,7 @@ final class ConfigurationClass {
private String beanName;
private final boolean imported;
private final ConfigurationClass importedBy;
/**
@@ -66,28 +67,28 @@ final class ConfigurationClass {
* @param metadataReader reader used to parse the underlying {@link Class}
* @param beanName must not be {@code null}
* @throws IllegalArgumentException if beanName is null (as of Spring 3.1.1)
* @see ConfigurationClass#ConfigurationClass(Class, boolean)
* @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
*/
public ConfigurationClass(MetadataReader metadataReader, String beanName) {
Assert.hasText(beanName, "bean name must not be null");
this.metadata = metadataReader.getAnnotationMetadata();
this.resource = metadataReader.getResource();
this.beanName = beanName;
this.imported = false;
this.importedBy = null;
}
/**
* Create a new {@link ConfigurationClass} representing a class that was imported
* using the {@link Import} annotation or automatically processed as a nested
* configuration class (if imported is {@code true}).
* configuration class (if importedBy is not {@code null}).
* @param metadataReader reader used to parse the underlying {@link Class}
* @param imported whether the given configuration class is being imported
* @param importedBy the configuration class importing this one or {@code null}
* @since 3.1.1
*/
public ConfigurationClass(MetadataReader metadataReader, boolean imported) {
public ConfigurationClass(MetadataReader metadataReader, ConfigurationClass importedBy) {
this.metadata = metadataReader.getAnnotationMetadata();
this.resource = metadataReader.getResource();
this.imported = imported;
this.importedBy = importedBy;
}
/**
@@ -95,14 +96,14 @@ final class ConfigurationClass {
* @param clazz the underlying {@link Class} to represent
* @param beanName name of the {@code @Configuration} class bean
* @throws IllegalArgumentException if beanName is null (as of Spring 3.1.1)
* @see ConfigurationClass#ConfigurationClass(Class, boolean)
* @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
*/
public ConfigurationClass(Class<?> clazz, String beanName) {
Assert.hasText(beanName, "bean name must not be null");
this.metadata = new StandardAnnotationMetadata(clazz, true);
this.resource = new DescriptiveResource(clazz.toString());
this.beanName = beanName;
this.imported = false;
this.importedBy = null;
}
/**
@@ -110,13 +111,13 @@ final class ConfigurationClass {
* using the {@link Import} annotation or automatically processed as a nested
* configuration class (if imported is {@code true}).
* @param clazz the underlying {@link Class} to represent
* @param imported whether the given configuration class is being imported
* @param importedBy the configuration class importing this one or {@code null}
* @since 3.1.1
*/
public ConfigurationClass(Class<?> clazz, boolean imported) {
public ConfigurationClass(Class<?> clazz, ConfigurationClass importedBy) {
this.metadata = new StandardAnnotationMetadata(clazz, true);
this.resource = new DescriptiveResource(clazz.toString());
this.imported = imported;
this.importedBy = importedBy;
}
@@ -136,9 +137,20 @@ final class ConfigurationClass {
* Return whether this configuration class was registered via @{@link Import} or
* automatically registered due to being nested within another configuration class.
* @since 3.1.1
* @see #getImportedBy()
*/
public boolean isImported() {
return this.imported;
return this.importedBy != null;
}
/**
* Returns the configuration class that imported this class or {@code null} if
* this configuration was not imported.
* @since 4.0
* @see #isImported()
*/
public ConfigurationClass getImportedBy() {
return importedBy;
}
public void setBeanName(String beanName) {

View File

@@ -16,6 +16,8 @@
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;
@@ -26,7 +28,6 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
import org.springframework.beans.factory.annotation.Autowire;
@@ -51,8 +52,6 @@ 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.
@@ -63,6 +62,7 @@ import static org.springframework.context.annotation.MetadataUtils.*;
*
* @author Chris Beams
* @author Juergen Hoeller
* @author Phillip Webb
* @since 3.0
* @see ConfigurationClassParser
*/
@@ -118,7 +118,7 @@ class ConfigurationClassBeanDefinitionReader {
* Read a particular {@link ConfigurationClass}, registering bean definitions for the
* class itself, all its {@link Bean} methods
*/
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) {
public void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) {
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
@@ -153,6 +153,10 @@ class ConfigurationClassBeanDefinitionReader {
* with the BeanDefinitionRegistry based on its contents.
*/
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
if (ConditionalAnnotationHelper.shouldSkip(beanMethod, this.registry,
this.environment, this.importBeanNameGenerator)) {
return;
}
ConfigurationClass configClass = beanMethod.getConfigurationClass();
MethodMetadata metadata = beanMethod.getMetadata();

View File

@@ -76,6 +76,7 @@ import static org.springframework.context.annotation.MetadataUtils.*;
*
* @author Chris Beams
* @author Juergen Hoeller
* @author Phillip Webb
* @since 3.0
* @see ConfigurationClassBeanDefinitionReader
*/
@@ -103,6 +104,8 @@ class ConfigurationClassParser {
private final ComponentScanAnnotationParser componentScanParser;
private final BeanNameGenerator beanNameGenerator;
/**
* Create a new {@link ConfigurationClassParser} instance that will be used
@@ -117,6 +120,7 @@ class ConfigurationClassParser {
this.environment = environment;
this.resourceLoader = resourceLoader;
this.registry = registry;
this.beanNameGenerator = componentScanBeanNameGenerator;
this.componentScanParser = new ComponentScanAnnotationParser(
resourceLoader, environment, componentScanBeanNameGenerator, registry);
}
@@ -144,11 +148,10 @@ class ConfigurationClassParser {
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
AnnotationMetadata metadata = configClass.getMetadata();
if (this.environment != null && metadata.isAnnotated(Profile.class.getName())) {
AnnotationAttributes profile = MetadataUtils.attributesFor(metadata, Profile.class);
if (!this.environment.acceptsProfiles(profile.getStringArray("value"))) {
return;
}
if (ConditionalAnnotationHelper.shouldSkip(configClass, this.registry,
this.environment, this.beanNameGenerator)) {
return;
}
// recursively process the configuration class and its superclass hierarchy
@@ -173,7 +176,7 @@ class ConfigurationClassParser {
ConfigurationClass configClass, AnnotationMetadata metadata) throws IOException {
// recursively process any member (nested) classes first
processMemberClasses(metadata);
processMemberClasses(configClass, metadata);
// process any @PropertySource annotations
AnnotationAttributes propertySource = attributesFor(metadata, org.springframework.context.annotation.PropertySource.class);
@@ -256,11 +259,12 @@ class ConfigurationClassParser {
* @param metadata the metadata representation of the containing class
* @throws IOException if there is any problem reading metadata from a member class
*/
private void processMemberClasses(AnnotationMetadata metadata) throws IOException {
private void processMemberClasses(ConfigurationClass configClass,
AnnotationMetadata metadata) throws IOException {
if (metadata instanceof StandardAnnotationMetadata) {
for (Class<?> memberClass : ((StandardAnnotationMetadata) metadata).getIntrospectedClass().getDeclaredClasses()) {
if (ConfigurationClassUtils.isConfigurationCandidate(new StandardAnnotationMetadata(memberClass))) {
processConfigurationClass(new ConfigurationClass(memberClass, true));
processConfigurationClass(new ConfigurationClass(memberClass, configClass));
}
}
}
@@ -269,7 +273,7 @@ class ConfigurationClassParser {
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(memberClassName);
AnnotationMetadata memberClassMetadata = reader.getAnnotationMetadata();
if (ConfigurationClassUtils.isConfigurationCandidate(memberClassMetadata)) {
processConfigurationClass(new ConfigurationClass(reader, true));
processConfigurationClass(new ConfigurationClass(reader, configClass));
}
}
}
@@ -391,9 +395,11 @@ class ConfigurationClassParser {
}
else {
// candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> process it as a @Configuration class
this.importStack.registerImport(importingClassMetadata.getClassName(), (candidate instanceof Class ? ((Class) candidate).getName() : (String) candidate));
processConfigurationClass(candidateToCheck instanceof Class ? new ConfigurationClass((Class) candidateToCheck, true) :
new ConfigurationClass((MetadataReader) candidateToCheck, true));
this.importStack.registerImport(importingClassMetadata.getClassName(),
(candidate instanceof Class ? ((Class) candidate).getName() : (String) candidate));
processConfigurationClass((candidateToCheck instanceof Class ?
new ConfigurationClass((Class) candidateToCheck, configClass) :
new ConfigurationClass((MetadataReader) candidateToCheck, configClass)));
}
}
}

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.
@@ -82,6 +82,7 @@ import static org.springframework.context.annotation.AnnotationConfigUtils.*;
*
* @author Chris Beams
* @author Juergen Hoeller
* @author Phillip Webb
* @since 3.0
*/
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
@@ -312,7 +313,12 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
registry, this.sourceExtractor, this.problemReporter, this.metadataReaderFactory,
this.resourceLoader, this.environment, this.importBeanNameGenerator);
}
this.reader.loadBeanDefinitions(parser.getConfigurationClasses());
for (ConfigurationClass configurationClass : parser.getConfigurationClasses()) {
if (!ConditionalAnnotationHelper.shouldSkip(configurationClass, registry,
this.environment, this.importBeanNameGenerator)) {
reader.loadBeanDefinitionsForConfigurationClass(configurationClass);
}
}
// 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.
@@ -38,8 +38,9 @@ import org.springframework.core.env.ConfigurableEnvironment;
* <p>The {@code @Profile} annotation may be used in any of the following ways:
* <ul>
* <li>as a type-level annotation on any class directly or indirectly annotated with
* {@code @Component}, including {@link Configuration @Configuration} classes
* <li>as a meta-annotation, for the purpose of composing custom stereotype annotations
* {@code @Component}, including {@link Configuration @Configuration} classes</li>
* <li>as a meta-annotation, for the purpose of composing custom stereotype annotations</li>
* <li>as a method-level annotation on any {@link Bean @Bean} method</li>
* </ul>
*
* <p>If a {@code @Configuration} class is marked with {@code @Profile}, all of the
@@ -65,6 +66,7 @@ import org.springframework.core.env.ConfigurableEnvironment;
* {@code spring-beans} XSD (version 3.1 or greater) for details.
*
* @author Chris Beams
* @author Phillip Webb
* @since 3.1
* @see ConfigurableEnvironment#setActiveProfiles
* @see ConfigurableEnvironment#setDefaultProfiles
@@ -72,7 +74,8 @@ import org.springframework.core.env.ConfigurableEnvironment;
* @see AbstractEnvironment#DEFAULT_PROFILES_PROPERTY_NAME
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Conditional(ProfileCondition.class)
public @interface Profile {
/**

View File

@@ -0,0 +1,42 @@
/*
* 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 org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* {@link Condition} that matches based on the value of a {@link Profile @Profile}
* annotation.
*
* @author Chris Beams
* @author Phillip Webb
* @since 4.0
*/
class ProfileCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
if (context.getEnvironment() != null && metadata.isAnnotated(Profile.class.getName())) {
AnnotationAttributes profile = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(Profile.class.getName()));
if (!context.getEnvironment().acceptsProfiles(profile.getStringArray("value"))) {
return false;
}
}
return true;
}
}