diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java b/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java
index cfbc4237141..b660b0355b2 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java
@@ -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());
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java
index b3f9450f983..a016b885545 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java
@@ -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;
}
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/Condition.java b/spring-context/src/main/java/org/springframework/context/annotation/Condition.java
new file mode 100644
index 00000000000..69543830aab
--- /dev/null
+++ b/spring-context/src/main/java/org/springframework/context/annotation/Condition.java
@@ -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.
+ *
+ *
Conditions are checked immediately before a component bean-definition is due to be
+ * registered and are free to veto registration based on any criteria that can be
+ * determined at that point.
+ *
+ *
Conditions 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);
+
+}
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConditionContext.java b/spring-context/src/main/java/org/springframework/context/annotation/ConditionContext.java
new file mode 100644
index 00000000000..564586501c1
--- /dev/null
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ConditionContext.java
@@ -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();
+
+}
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/Conditional.java b/spring-context/src/main/java/org/springframework/context/annotation/Conditional.java
new file mode 100644
index 00000000000..9a987142547
--- /dev/null
+++ b/spring-context/src/main/java/org/springframework/context/annotation/Conditional.java
@@ -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.
+ *
+ *
A condition is any state that can be determined programmatically
+ * immediately before the bean is due to be created (see {@link Condition} for details).
+ *
+ *
The {@code @Conditional} annotation may be used in any of the following ways:
+ *
+ *
as a type-level annotation on any class directly or indirectly annotated with
+ * {@code @Component}, including {@link Configuration @Configuration} classes
+ *
as a meta-annotation, for the purpose of composing custom stereotype
+ * annotations
+ *
as a method-level annotation on any {@link Bean @Bean} method
+ *
+ *
+ *
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();
+
+}
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConditionalAnnotationHelper.java b/spring-context/src/main/java/org/springframework/context/annotation/ConditionalAnnotationHelper.java
new file mode 100644
index 00000000000..acd766ea48b
--- /dev/null
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ConditionalAnnotationHelper.java
@@ -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 getConditionClasses(AnnotatedTypeMetadata metadata) {
+ MultiValueMap attributes = metadata.getAllAnnotationAttributes(
+ CONDITIONAL_ANNOTATION, true);
+ Object values = attributes == null ? null : attributes.get("value");
+ return (List) (values == null ? Collections.emptyList() : values);
+ }
+
+ private static Condition getCondition(String conditionClassName,
+ ClassLoader classloader) {
+ Class> conditionClass = ClassUtils.resolveClassName(conditionClassName,
+ classloader);
+ return (Condition) BeanUtils.instantiateClass(conditionClass);
+ }
+
+
+ /**
+ * 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());
+ }
+ }
+
+}
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java
index 0a5a6460924..6ed1f643093 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java
@@ -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) {
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java
index 27ae171955c..eab2b0bd26b 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java
@@ -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();
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
index 82fbbeba528..5b1736fd6f2 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
@@ -25,6 +25,8 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
@@ -32,19 +34,23 @@ import java.util.Stack;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.Aware;
import org.springframework.beans.factory.BeanClassLoaderAware;
+import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.parsing.Location;
import org.springframework.beans.factory.parsing.Problem;
import org.springframework.beans.factory.parsing.ProblemReporter;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.NestedIOException;
import org.springframework.core.annotation.AnnotationAttributes;
+import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
@@ -76,11 +82,21 @@ import static org.springframework.context.annotation.MetadataUtils.*;
*
* @author Chris Beams
* @author Juergen Hoeller
+ * @author Phillip Webb
* @since 3.0
* @see ConfigurationClassBeanDefinitionReader
*/
class ConfigurationClassParser {
+ private static final Comparator DEFERRED_IMPORT_COMPARATOR =
+ new Comparator() {
+ public int compare(DeferredImportSelectorHolder o1,
+ DeferredImportSelectorHolder o2) {
+ return AnnotationAwareOrderComparator.INSTANCE.compare(
+ o1.getImportSelector(), o2.getImportSelector());
+ }
+ };
+
private final MetadataReaderFactory metadataReaderFactory;
private final ProblemReporter problemReporter;
@@ -103,6 +119,10 @@ class ConfigurationClassParser {
private final ComponentScanAnnotationParser componentScanParser;
+ private final BeanNameGenerator beanNameGenerator;
+
+ private final List deferredImportSelectors =
+ new LinkedList();
/**
* Create a new {@link ConfigurationClassParser} instance that will be used
@@ -117,10 +137,28 @@ class ConfigurationClassParser {
this.environment = environment;
this.resourceLoader = resourceLoader;
this.registry = registry;
+ this.beanNameGenerator = componentScanBeanNameGenerator;
this.componentScanParser = new ComponentScanAnnotationParser(
resourceLoader, environment, componentScanBeanNameGenerator, registry);
}
+ public void parse(Set configCandidates) {
+ for (BeanDefinitionHolder holder : configCandidates) {
+ BeanDefinition bd = holder.getBeanDefinition();
+ try {
+ if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
+ parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
+ }
+ else {
+ parse(bd.getBeanClassName(), holder.getBeanName());
+ }
+ }
+ catch (IOException ex) {
+ throw new BeanDefinitionStoreException("Failed to load bean class: " + bd.getBeanClassName(), ex);
+ }
+ }
+ processDeferredImportSelectors();
+ }
/**
* Parse the specified {@link Configuration @Configuration} class.
@@ -138,17 +176,16 @@ class ConfigurationClassParser {
* @param clazz the Class to parse
* @param beanName must not be null (as of Spring 3.1.1)
*/
- public void parse(Class> clazz, String beanName) throws IOException {
+ protected void parse(Class> clazz, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(clazz, beanName));
}
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 +210,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 +293,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 +307,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));
}
}
}
@@ -365,6 +403,21 @@ class ConfigurationClassParser {
}
}
+ private void processDeferredImportSelectors() {
+ Collections.sort(this.deferredImportSelectors, DEFERRED_IMPORT_COMPARATOR);
+ for (DeferredImportSelectorHolder deferredImport : this.deferredImportSelectors) {
+ try {
+ ConfigurationClass configClass = deferredImport.getConfigurationClass();
+ String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
+ processImport(configClass, Arrays.asList(imports), false);
+ }
+ catch (IOException ex) {
+ throw new BeanDefinitionStoreException("Failed to load bean class: ", ex);
+ }
+ }
+ deferredImportSelectors.clear();
+ }
+
private void processImport(ConfigurationClass configClass, Collection> classesToImport, boolean checkForCircularImports) throws IOException {
if (checkForCircularImports && this.importStack.contains(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack, configClass.getMetadata()));
@@ -380,7 +433,12 @@ class ConfigurationClassParser {
Class> candidateClass = (candidate instanceof Class ? (Class) candidate : this.resourceLoader.getClassLoader().loadClass((String) candidate));
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
invokeAwareMethods(selector);
- processImport(configClass, Arrays.asList(selector.selectImports(importingClassMetadata)), false);
+ if(selector instanceof DeferredImportSelector) {
+ this.deferredImportSelectors.add(new DeferredImportSelectorHolder(
+ configClass, (DeferredImportSelector) selector));
+ } else {
+ processImport(configClass, Arrays.asList(selector.selectImports(importingClassMetadata)), false);
+ }
}
else if (checkAssignability(ImportBeanDefinitionRegistrar.class, candidateToCheck)) {
// the candidate class is an ImportBeanDefinitionRegistrar -> delegate to it to register additional bean definitions
@@ -391,9 +449,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)));
}
}
}
@@ -533,4 +593,24 @@ class ConfigurationClassParser {
}
}
+
+ private static class DeferredImportSelectorHolder {
+
+ private ConfigurationClass configurationClass;
+
+ private DeferredImportSelector importSelector;
+
+ public DeferredImportSelectorHolder(ConfigurationClass configurationClass, DeferredImportSelector importSelector) {
+ this.configurationClass = configurationClass;
+ this.importSelector = importSelector;
+ }
+
+ public ConfigurationClass getConfigurationClass() {
+ return configurationClass;
+ }
+
+ public DeferredImportSelector getImportSelector() {
+ return importSelector;
+ }
+ }
}
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java
index 7afbc027337..dbd94f33103 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java
@@ -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,
@@ -275,20 +276,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
- for (BeanDefinitionHolder holder : configCandidates) {
- BeanDefinition bd = holder.getBeanDefinition();
- try {
- if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
- parser.parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
- }
- else {
- parser.parse(bd.getBeanClassName(), holder.getBeanName());
- }
- }
- catch (IOException ex) {
- throw new BeanDefinitionStoreException("Failed to load bean class: " + bd.getBeanClassName(), ex);
- }
- }
+ parser.parse(configCandidates);
parser.validate();
// Handle any @PropertySource annotations
@@ -312,7 +300,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) {
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/DeferredImportSelector.java b/spring-context/src/main/java/org/springframework/context/annotation/DeferredImportSelector.java
new file mode 100644
index 00000000000..2c104126f9b
--- /dev/null
+++ b/spring-context/src/main/java/org/springframework/context/annotation/DeferredImportSelector.java
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+/**
+ * A variation of {@link ImportSelector} that runs after all {@code @Configuration} beans
+ * have been processed. This type of selector can be particularly useful when the selected
+ * imports are {@code @Conditional}.
+ *
+ *