diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java index 967f8f231e..d2210c812f 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java @@ -411,7 +411,8 @@ class ConfigurationClassEnhancer { Field field = ReflectionUtils.findField(enhancedConfigInstance.getClass(), BEAN_FACTORY_FIELD); Assert.state(field != null, "Unable to find generated bean factory field"); Object beanFactory = ReflectionUtils.getField(field, enhancedConfigInstance); - Assert.isInstanceOf(ConfigurableBeanFactory.class, beanFactory); + Assert.state(beanFactory != null, "The BeanFactory has not been injected into the @Configuration class"); + Assert.state(beanFactory instanceof ConfigurableBeanFactory, "The injected BeanFactory is not a ConfigurableBeanFactory"); return (ConfigurableBeanFactory) beanFactory; } } 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 555e78b7fa..f659878fb0 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 @@ -16,6 +16,7 @@ package org.springframework.context.annotation; +import java.beans.PropertyDescriptor; import java.io.IOException; import java.util.HashSet; import java.util.LinkedHashMap; @@ -27,15 +28,18 @@ 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.PropertyValues; 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.annotation.AutowiredAnnotationBeanPostProcessor; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter; import org.springframework.beans.factory.config.SingletonBeanRegistry; import org.springframework.beans.factory.parsing.FailFastProblemReporter; import org.springframework.beans.factory.parsing.PassThroughSourceExtractor; @@ -50,6 +54,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.EnvironmentAware; import org.springframework.context.ResourceLoaderAware; +import org.springframework.context.annotation.ConfigurationClassEnhancer.EnhancedConfiguration; import org.springframework.context.annotation.ConfigurationClassParser.ImportRegistry; import org.springframework.core.Ordered; import org.springframework.core.PriorityOrdered; @@ -96,6 +101,9 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo private static final String IMPORT_REGISTRY_BEAN_NAME = ConfigurationClassPostProcessor.class.getName() + ".importRegistry"; + private static final String ENHANCED_CONFIGURATION_PROCESSOR_BEAN_NAME = + ConfigurationClassPostProcessor.class.getName() + ".enhancedConfigurationProcessor"; + private final Log logger = LogFactory.getLog(getClass()); @@ -225,6 +233,10 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo iabpp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); registry.registerBeanDefinition(IMPORT_AWARE_PROCESSOR_BEAN_NAME, iabpp); + RootBeanDefinition ecbpp = new RootBeanDefinition(EnhancedConfigurationBeanPostProcessor.class); + ecbpp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + registry.registerBeanDefinition(ENHANCED_CONFIGURATION_PROCESSOR_BEAN_NAME, ecbpp); + int registryId = System.identityHashCode(registry); if (this.registriesPostProcessed.contains(registryId)) { throw new IllegalStateException( @@ -424,4 +436,40 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo } } + + /** + * {@link InstantiationAwareBeanPostProcessorAdapter} that ensures + * {@link EnhancedConfiguration} beans are injected with the {@link BeanFactory} + * before the {@link AutowiredAnnotationBeanPostProcessor} runs (SPR-10668). + */ + private static class EnhancedConfigurationBeanPostProcessor extends + InstantiationAwareBeanPostProcessorAdapter implements PriorityOrdered, + BeanFactoryAware { + + private BeanFactory beanFactory; + + @Override + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE; + } + + @Override + public PropertyValues postProcessPropertyValues(PropertyValues pvs, + PropertyDescriptor[] pds, Object bean, String beanName) + throws BeansException { + // Inject the BeanFactory before AutowiredAnnotationBeanPostProcessor's + // postProcessPropertyValues method attempts to auto-wire other configuration + // beans. + if (bean instanceof EnhancedConfiguration) { + ((EnhancedConfiguration) bean).setBeanFactory(this.beanFactory); + } + return pvs; + } + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; + } + + } } diff --git a/spring-context/src/test/java/org/springframework/context/annotation/configuration/ScopingTests.java b/spring-context/src/test/java/org/springframework/context/annotation/configuration/ScopingTests.java index 52aaaac7e9..dcffc2c2ed 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/configuration/ScopingTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/configuration/ScopingTests.java @@ -193,8 +193,8 @@ public class ScopingTests { @Test public void testScopedConfigurationBeanDefinitionCount() throws Exception { // count the beans - // 6 @Beans + 1 Configuration + 2 scoped proxy + 1 importRegistry - assertEquals(10, ctx.getBeanDefinitionCount()); + // 6 @Beans + 1 Configuration + 2 scoped proxy + 1 importRegistry + 1 enhanced config post processor + assertEquals(11, ctx.getBeanDefinitionCount()); } // /** diff --git a/spring-context/src/test/java/org/springframework/context/annotation/configuration/Spr10668Tests.java b/spring-context/src/test/java/org/springframework/context/annotation/configuration/Spr10668Tests.java new file mode 100644 index 0000000000..d59e4133d5 --- /dev/null +++ b/spring-context/src/test/java/org/springframework/context/annotation/configuration/Spr10668Tests.java @@ -0,0 +1,78 @@ +/* + * 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.configuration; + +import org.junit.Test; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static org.junit.Assert.*; + +/** + * Tests for SPR-10668. + * + * @author Oliver Gierke + * @author Phillip Webb + */ +public class Spr10668Tests { + + @Test + public void testSelfInjectHierarchy() throws Exception { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + ChildConfig.class); + assertNotNull(context.getBean(MyComponent.class)); + context.close(); + } + + @Configuration + public static class ParentConfig implements BeanFactoryAware { + + @Autowired(required = false) + MyComponent component; + + public ParentConfig() { + System.out.println("Parent " + getClass()); + } + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + System.out.println("BFA " + getClass()); + } + + } + + @Configuration + public static class ChildConfig extends ParentConfig { + + @Bean + public MyComponentImpl myComponent() { + return new MyComponentImpl(); + } + + } + + public static interface MyComponent { + } + + public static class MyComponentImpl implements MyComponent { + } +}