Skip to content

Commit a403e8f

Browse files
committed
Inject @configuration BeanFactory before autowire
Add EnhancedConfigurationBeanPostProcessor to inject the BeanFactory into EnhancedConfiguration classes before the AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues method is called. Prior to this commit it was possible for @autowire in a @configuration class to invoke an enhanced configuration class method before the BeanFactory was injected. This is due to the fact that the AutowiredAnnotationBeanPostProcessor was called before AbstractAutowireCapableBeanFactory.invokeAwareMethods(). Issue: SPR-10668
1 parent 62e2336 commit a403e8f

File tree

4 files changed

+130
-3
lines changed

4 files changed

+130
-3
lines changed

spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,8 @@ private ConfigurableBeanFactory getBeanFactory(Object enhancedConfigInstance) {
411411
Field field = ReflectionUtils.findField(enhancedConfigInstance.getClass(), BEAN_FACTORY_FIELD);
412412
Assert.state(field != null, "Unable to find generated bean factory field");
413413
Object beanFactory = ReflectionUtils.getField(field, enhancedConfigInstance);
414-
Assert.isInstanceOf(ConfigurableBeanFactory.class, beanFactory);
414+
Assert.state(beanFactory != null, "The BeanFactory has not been injected into the @Configuration class");
415+
Assert.state(beanFactory instanceof ConfigurableBeanFactory, "The injected BeanFactory is not a ConfigurableBeanFactory");
415416
return (ConfigurableBeanFactory) beanFactory;
416417
}
417418
}

spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.context.annotation;
1818

19+
import java.beans.PropertyDescriptor;
1920
import java.io.IOException;
2021
import java.util.HashSet;
2122
import java.util.LinkedHashMap;
@@ -27,15 +28,18 @@
2728
import org.apache.commons.logging.Log;
2829
import org.apache.commons.logging.LogFactory;
2930
import org.springframework.beans.BeansException;
31+
import org.springframework.beans.PropertyValues;
3032
import org.springframework.beans.factory.BeanClassLoaderAware;
3133
import org.springframework.beans.factory.BeanDefinitionStoreException;
3234
import org.springframework.beans.factory.BeanFactory;
3335
import org.springframework.beans.factory.BeanFactoryAware;
36+
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
3437
import org.springframework.beans.factory.config.BeanDefinition;
3538
import org.springframework.beans.factory.config.BeanDefinitionHolder;
3639
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
3740
import org.springframework.beans.factory.config.BeanPostProcessor;
3841
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
42+
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
3943
import org.springframework.beans.factory.config.SingletonBeanRegistry;
4044
import org.springframework.beans.factory.parsing.FailFastProblemReporter;
4145
import org.springframework.beans.factory.parsing.PassThroughSourceExtractor;
@@ -50,6 +54,7 @@
5054
import org.springframework.context.ApplicationContextAware;
5155
import org.springframework.context.EnvironmentAware;
5256
import org.springframework.context.ResourceLoaderAware;
57+
import org.springframework.context.annotation.ConfigurationClassEnhancer.EnhancedConfiguration;
5358
import org.springframework.context.annotation.ConfigurationClassParser.ImportRegistry;
5459
import org.springframework.core.Ordered;
5560
import org.springframework.core.PriorityOrdered;
@@ -96,6 +101,9 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
96101
private static final String IMPORT_REGISTRY_BEAN_NAME =
97102
ConfigurationClassPostProcessor.class.getName() + ".importRegistry";
98103

104+
private static final String ENHANCED_CONFIGURATION_PROCESSOR_BEAN_NAME =
105+
ConfigurationClassPostProcessor.class.getName() + ".enhancedConfigurationProcessor";
106+
99107

100108
private final Log logger = LogFactory.getLog(getClass());
101109

@@ -225,6 +233,10 @@ public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
225233
iabpp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
226234
registry.registerBeanDefinition(IMPORT_AWARE_PROCESSOR_BEAN_NAME, iabpp);
227235

236+
RootBeanDefinition ecbpp = new RootBeanDefinition(EnhancedConfigurationBeanPostProcessor.class);
237+
ecbpp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
238+
registry.registerBeanDefinition(ENHANCED_CONFIGURATION_PROCESSOR_BEAN_NAME, ecbpp);
239+
228240
int registryId = System.identityHashCode(registry);
229241
if (this.registriesPostProcessed.contains(registryId)) {
230242
throw new IllegalStateException(
@@ -424,4 +436,40 @@ public int getOrder() {
424436
}
425437
}
426438

439+
440+
/**
441+
* {@link InstantiationAwareBeanPostProcessorAdapter} that ensures
442+
* {@link EnhancedConfiguration} beans are injected with the {@link BeanFactory}
443+
* before the {@link AutowiredAnnotationBeanPostProcessor} runs (SPR-10668).
444+
*/
445+
private static class EnhancedConfigurationBeanPostProcessor extends
446+
InstantiationAwareBeanPostProcessorAdapter implements PriorityOrdered,
447+
BeanFactoryAware {
448+
449+
private BeanFactory beanFactory;
450+
451+
@Override
452+
public int getOrder() {
453+
return Ordered.HIGHEST_PRECEDENCE;
454+
}
455+
456+
@Override
457+
public PropertyValues postProcessPropertyValues(PropertyValues pvs,
458+
PropertyDescriptor[] pds, Object bean, String beanName)
459+
throws BeansException {
460+
// Inject the BeanFactory before AutowiredAnnotationBeanPostProcessor's
461+
// postProcessPropertyValues method attempts to auto-wire other configuration
462+
// beans.
463+
if (bean instanceof EnhancedConfiguration) {
464+
((EnhancedConfiguration) bean).setBeanFactory(this.beanFactory);
465+
}
466+
return pvs;
467+
}
468+
469+
@Override
470+
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
471+
this.beanFactory = beanFactory;
472+
}
473+
474+
}
427475
}

spring-context/src/test/java/org/springframework/context/annotation/configuration/ScopingTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,8 @@ public void testScopedProxyConfigurationWithClasses() throws Exception {
193193
@Test
194194
public void testScopedConfigurationBeanDefinitionCount() throws Exception {
195195
// count the beans
196-
// 6 @Beans + 1 Configuration + 2 scoped proxy + 1 importRegistry
197-
assertEquals(10, ctx.getBeanDefinitionCount());
196+
// 6 @Beans + 1 Configuration + 2 scoped proxy + 1 importRegistry + 1 enhanced config post processor
197+
assertEquals(11, ctx.getBeanDefinitionCount());
198198
}
199199

200200
// /**
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.context.annotation.configuration;
18+
19+
import org.junit.Test;
20+
import org.springframework.beans.BeansException;
21+
import org.springframework.beans.factory.BeanFactory;
22+
import org.springframework.beans.factory.BeanFactoryAware;
23+
import org.springframework.beans.factory.annotation.Autowired;
24+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
25+
import org.springframework.context.annotation.Bean;
26+
import org.springframework.context.annotation.Configuration;
27+
28+
import static org.junit.Assert.*;
29+
30+
/**
31+
* Tests for SPR-10668.
32+
*
33+
* @author Oliver Gierke
34+
* @author Phillip Webb
35+
*/
36+
public class Spr10668Tests {
37+
38+
@Test
39+
public void testSelfInjectHierarchy() throws Exception {
40+
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
41+
ChildConfig.class);
42+
assertNotNull(context.getBean(MyComponent.class));
43+
context.close();
44+
}
45+
46+
@Configuration
47+
public static class ParentConfig implements BeanFactoryAware {
48+
49+
@Autowired(required = false)
50+
MyComponent component;
51+
52+
public ParentConfig() {
53+
System.out.println("Parent " + getClass());
54+
}
55+
56+
@Override
57+
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
58+
System.out.println("BFA " + getClass());
59+
}
60+
61+
}
62+
63+
@Configuration
64+
public static class ChildConfig extends ParentConfig {
65+
66+
@Bean
67+
public MyComponentImpl myComponent() {
68+
return new MyComponentImpl();
69+
}
70+
71+
}
72+
73+
public static interface MyComponent {
74+
}
75+
76+
public static class MyComponentImpl implements MyComponent {
77+
}
78+
}

0 commit comments

Comments
 (0)