Skip to content

Commit 6b2b5c4

Browse files
committed
introduced BeanDefinitionRegistryPostProcessor extension to BeanFactoryPostProcessor; @configuration classes support definition of BeanFactoryPostProcessor beans as well (SPR-6455, SPR-6611)
1 parent 2d6ea2f commit 6b2b5c4

File tree

6 files changed

+128
-33
lines changed

6 files changed

+128
-33
lines changed

org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import org.apache.commons.logging.Log;
2929
import org.apache.commons.logging.LogFactory;
30+
3031
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
3132
import org.springframework.beans.factory.annotation.Autowire;
3233
import org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor;
@@ -48,7 +49,6 @@
4849
import org.springframework.core.type.AnnotationMetadata;
4950
import org.springframework.core.type.MethodMetadata;
5051
import org.springframework.core.type.StandardAnnotationMetadata;
51-
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
5252
import org.springframework.core.type.classreading.MetadataReader;
5353
import org.springframework.core.type.classreading.MetadataReaderFactory;
5454
import org.springframework.stereotype.Component;
@@ -68,11 +68,11 @@
6868
*/
6969
class ConfigurationClassBeanDefinitionReader {
7070

71-
static final String CONFIGURATION_CLASS_FULL = "full";
71+
private static final String CONFIGURATION_CLASS_FULL = "full";
7272

73-
static final String CONFIGURATION_CLASS_LITE = "lite";
73+
private static final String CONFIGURATION_CLASS_LITE = "lite";
7474

75-
static final String CONFIGURATION_CLASS_ATTRIBUTE =
75+
private static final String CONFIGURATION_CLASS_ATTRIBUTE =
7676
Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "configurationClass");
7777

7878
private static final Log logger = LogFactory.getLog(ConfigurationClassBeanDefinitionReader.class);
@@ -86,7 +86,6 @@ class ConfigurationClassBeanDefinitionReader {
8686
private final MetadataReaderFactory metadataReaderFactory;
8787

8888

89-
9089
/**
9190
* Create a new {@link ConfigurationClassBeanDefinitionReader} instance that will be used
9291
* to populate the given {@link BeanDefinitionRegistry}.
@@ -95,6 +94,7 @@ class ConfigurationClassBeanDefinitionReader {
9594
*/
9695
public ConfigurationClassBeanDefinitionReader(BeanDefinitionRegistry registry, SourceExtractor sourceExtractor,
9796
ProblemReporter problemReporter, MetadataReaderFactory metadataReaderFactory) {
97+
9898
this.registry = registry;
9999
this.sourceExtractor = sourceExtractor;
100100
this.problemReporter = problemReporter;
@@ -145,13 +145,15 @@ private void doLoadBeanDefinitionForConfigurationClassIfNecessary(ConfigurationC
145145
if (logger.isDebugEnabled()) {
146146
logger.debug(String.format("Registered bean definition for imported @Configuration class %s", configBeanName));
147147
}
148-
} else {
148+
}
149+
else {
149150
try {
150151
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
151152
AnnotationMetadata metadata = reader.getAnnotationMetadata();
152153
this.problemReporter.error(
153154
new InvalidConfigurationImportProblem(className, reader.getResource(), metadata));
154-
} catch (IOException ex) {
155+
}
156+
catch (IOException ex) {
155157
throw new IllegalStateException("Could not create MetadataReader for class " + className);
156158
}
157159
}
@@ -181,7 +183,7 @@ private void loadBeanDefinitionsForModelMethod(ConfigurationClassMethod method)
181183
this.registry.registerAlias(beanName, alias);
182184
}
183185

184-
// has this already been overriden (i.e.: via XML)?
186+
// has this already been overridden (e.g. via XML)?
185187
if (this.registry.containsBeanDefinition(beanName)) {
186188
BeanDefinition existingBeanDef = registry.getBeanDefinition(beanName);
187189
// is the existing bean definition one that was created from a configuration class?
@@ -277,14 +279,15 @@ private void loadBeanDefinitionsFromImportedResources(Map<String, Class<?>> impo
277279
}
278280
}
279281

282+
280283
/**
281284
* Check whether the given bean definition is a candidate for a configuration class,
282285
* and mark it accordingly.
283286
* @param beanDef the bean definition to check
284287
* @param metadataReaderFactory the current factory in use by the caller
285288
* @return whether the candidate qualifies as (any kind of) configuration class
286289
*/
287-
static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
290+
public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
288291
AnnotationMetadata metadata = null;
289292

290293
// Check already loaded Class if present...
@@ -322,14 +325,22 @@ else if (metadata.isAnnotated(Component.class.getName()) ||
322325
return false;
323326
}
324327

328+
/**
329+
* Determine whether the given bean definition indicates a full @Configuration class.
330+
*/
331+
public static boolean isFullConfigurationClass(BeanDefinition beanDef) {
332+
return CONFIGURATION_CLASS_FULL.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE));
333+
}
334+
335+
325336
/**
326337
* {@link RootBeanDefinition} marker subclass used to signify that a bean definition
327338
* created from a configuration class as opposed to any other configuration source.
328339
* Used in bean overriding cases where it's necessary to determine whether the bean
329340
* definition was created externally.
330341
*/
331342
@SuppressWarnings("serial")
332-
private class ConfigurationClassBeanDefinition extends RootBeanDefinition implements AnnotatedBeanDefinition {
343+
private static class ConfigurationClassBeanDefinition extends RootBeanDefinition implements AnnotatedBeanDefinition {
333344

334345
private AnnotationMetadata annotationMetadata;
335346

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

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,6 @@
1616

1717
package org.springframework.context.annotation;
1818

19-
import static org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.CONFIGURATION_CLASS_ATTRIBUTE;
20-
import static org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.CONFIGURATION_CLASS_FULL;
21-
2219
import java.io.IOException;
2320
import java.util.LinkedHashMap;
2421
import java.util.LinkedHashSet;
@@ -40,6 +37,7 @@
4037
import org.springframework.beans.factory.parsing.SourceExtractor;
4138
import org.springframework.beans.factory.support.AbstractBeanDefinition;
4239
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
40+
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
4341
import org.springframework.core.Ordered;
4442
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
4543
import org.springframework.core.type.classreading.MetadataReaderFactory;
@@ -63,7 +61,7 @@
6361
* @author Juergen Hoeller
6462
* @since 3.0
6563
*/
66-
public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor, BeanClassLoaderAware {
64+
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor, BeanClassLoaderAware {
6765

6866
/** Whether the CGLIB2 library is present on the classpath */
6967
private static final boolean cglibAvailable = ClassUtils.isPresent(
@@ -124,16 +122,18 @@ public int getOrder() {
124122
}
125123

126124

125+
/**
126+
* Derive further bean definitions from the configuration classes in the registry.
127+
*/
128+
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
129+
processConfigBeanDefinitions(registry);
130+
}
131+
127132
/**
128133
* Prepare the Configuration classes for servicing bean requests at runtime
129134
* by replacing them with CGLIB-enhanced subclasses.
130135
*/
131136
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
132-
if (!(beanFactory instanceof BeanDefinitionRegistry)) {
133-
throw new IllegalStateException(
134-
"ConfigurationClassPostProcessor expects a BeanFactory that implements BeanDefinitionRegistry");
135-
}
136-
processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
137137
enhanceConfigurationClasses(beanFactory);
138138
}
139139

@@ -174,8 +174,8 @@ public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
174174
parser.validate();
175175

176176
// Read the model and create bean definitions based on its content
177-
ConfigurationClassBeanDefinitionReader reader =
178-
new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.problemReporter, this.metadataReaderFactory);
177+
ConfigurationClassBeanDefinitionReader reader = new ConfigurationClassBeanDefinitionReader(
178+
registry, this.sourceExtractor, this.problemReporter, this.metadataReaderFactory);
179179
reader.loadBeanDefinitions(parser.getConfigurationClasses());
180180
}
181181

@@ -189,7 +189,7 @@ public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFact
189189
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<String, AbstractBeanDefinition>();
190190
for (String beanName : beanFactory.getBeanDefinitionNames()) {
191191
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
192-
if (CONFIGURATION_CLASS_FULL.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE))) {
192+
if (ConfigurationClassBeanDefinitionReader.isFullConfigurationClass(beanDef)) {
193193
if (!(beanDef instanceof AbstractBeanDefinition)) {
194194
throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
195195
beanName + "' since it is not stored in an AbstractBeanDefinition subclass");

org.springframework.context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2009 the original author or authors.
2+
* Copyright 2002-2010 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -39,6 +39,8 @@
3939
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
4040
import org.springframework.beans.factory.config.BeanPostProcessor;
4141
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
42+
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
43+
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
4244
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
4345
import org.springframework.beans.support.ResourceEditorRegistrar;
4446
import org.springframework.context.ApplicationContext;
@@ -569,6 +571,21 @@ protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactor
569571
* <p>Must be called before singleton instantiation.
570572
*/
571573
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
574+
// Invoke BeanDefinitionRegistryPostProcessors first, if any.
575+
if (beanFactory instanceof BeanDefinitionRegistry) {
576+
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
577+
for (BeanFactoryPostProcessor postProcessor : getBeanFactoryPostProcessors()) {
578+
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
579+
((BeanDefinitionRegistryPostProcessor) postProcessor).postProcessBeanDefinitionRegistry(registry);
580+
}
581+
}
582+
Collection<BeanDefinitionRegistryPostProcessor> registryPostProcessors =
583+
beanFactory.getBeansOfType(BeanDefinitionRegistryPostProcessor.class, true, false).values();
584+
for (BeanDefinitionRegistryPostProcessor postProcessor : registryPostProcessors) {
585+
postProcessor.postProcessBeanDefinitionRegistry(registry);
586+
}
587+
}
588+
572589
// Invoke factory processors registered with the context instance.
573590
invokeBeanFactoryPostProcessors(getBeanFactoryPostProcessors(), beanFactory);
574591

org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2009 the original author or authors.
2+
* Copyright 2002-2010 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -26,9 +26,13 @@
2626
import org.springframework.beans.factory.annotation.Required;
2727
import org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor;
2828
import org.springframework.beans.factory.config.BeanDefinition;
29+
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
30+
import org.springframework.beans.factory.config.BeanPostProcessor;
31+
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
2932
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
3033
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
3134
import org.springframework.beans.factory.support.RootBeanDefinition;
35+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
3236
import org.springframework.context.annotation.AnnotationConfigUtils;
3337
import org.springframework.context.annotation.Bean;
3438
import org.springframework.context.annotation.Configuration;
@@ -58,6 +62,7 @@ private BeanFactory initBeanFactory(Class<?>... configClasses) {
5862
factory.registerBeanDefinition(configBeanName, new RootBeanDefinition(configClass));
5963
}
6064
ConfigurationClassPostProcessor ccpp = new ConfigurationClassPostProcessor();
65+
ccpp.postProcessBeanDefinitionRegistry(factory);
6166
ccpp.postProcessBeanFactory(factory);
6267
RequiredAnnotationBeanPostProcessor rapp = new RequiredAnnotationBeanPostProcessor();
6368
rapp.setBeanFactory(factory);
@@ -123,6 +128,19 @@ public void configurationWithPrototypeScopedBeans() {
123128
assertNotSame(bar.getSpouse(), baz);
124129
}
125130

131+
@Test
132+
public void configurationWithPostProcessor() {
133+
BeanFactory factory = new AnnotationConfigApplicationContext(ConfigWithPostProcessor.class);
134+
135+
TestBean foo = factory.getBean("foo", TestBean.class);
136+
ITestBean bar = factory.getBean("bar", ITestBean.class);
137+
ITestBean baz = factory.getBean("baz", ITestBean.class);
138+
139+
assertEquals("foo-processed", foo.getName());
140+
assertEquals("bar-processed", bar.getName());
141+
assertEquals("baz-processed", baz.getName());
142+
}
143+
126144

127145
@Configuration
128146
static class ConfigWithBeanWithCustomName {
@@ -152,7 +170,9 @@ static class SimplestPossibleConfig {
152170

153171
@Configuration
154172
static class ConfigWithBeanWithAliases {
173+
155174
static TestBean testBean = new TestBean();
175+
156176
@Bean(name={"name1", "alias1", "alias2", "alias3"})
157177
public TestBean methodName() {
158178
return testBean;
@@ -177,7 +197,40 @@ static class ConfigWithPrototypeBean {
177197

178198
@Bean @Scope("prototype")
179199
public TestBean baz() {
180-
return new TestBean("bar");
200+
return new TestBean("baz");
201+
}
202+
}
203+
204+
205+
static class ConfigWithPostProcessor extends ConfigWithPrototypeBean {
206+
207+
@Bean
208+
public BeanPostProcessor beanPostProcessor() {
209+
return new BeanPostProcessor() {
210+
String nameSuffix;
211+
public void setNameSuffix(String nameSuffix) {
212+
this.nameSuffix = nameSuffix;
213+
}
214+
public Object postProcessBeforeInitialization(Object bean, String beanName) {
215+
if (bean instanceof ITestBean) {
216+
((ITestBean) bean).setName(((ITestBean) bean).getName() + nameSuffix);
217+
}
218+
return bean;
219+
}
220+
public Object postProcessAfterInitialization(Object bean, String beanName) {
221+
return bean;
222+
}
223+
};
224+
}
225+
226+
@Bean
227+
public BeanFactoryPostProcessor beanFactoryPostProcessor() {
228+
return new BeanFactoryPostProcessor() {
229+
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
230+
BeanDefinition bd = beanFactory.getBeanDefinition("beanPostProcessor");
231+
bd.getPropertyValues().addPropertyValue("nameSuffix", "-processed");
232+
}
233+
};
181234
}
182235
}
183236

org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ImportResourceTests.java

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2009 the original author or authors.
2+
* Copyright 2002-2010 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,16 +16,17 @@
1616

1717
package org.springframework.context.annotation.configuration;
1818

19-
import static org.hamcrest.CoreMatchers.equalTo;
20-
import static org.junit.Assert.assertThat;
21-
import static org.junit.Assert.assertTrue;
22-
2319
import org.aspectj.lang.annotation.Aspect;
2420
import org.aspectj.lang.annotation.Before;
21+
import static org.hamcrest.CoreMatchers.*;
22+
import static org.junit.Assert.*;
2523
import org.junit.Ignore;
2624
import org.junit.Test;
25+
import test.beans.TestBean;
26+
2727
import org.springframework.aop.support.AopUtils;
2828
import org.springframework.beans.factory.annotation.Autowired;
29+
import org.springframework.beans.factory.annotation.Value;
2930
import org.springframework.beans.factory.support.PropertiesBeanDefinitionReader;
3031
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
3132
import org.springframework.context.ApplicationContext;
@@ -34,26 +35,29 @@
3435
import org.springframework.context.annotation.Configuration;
3536
import org.springframework.context.annotation.ImportResource;
3637

37-
import test.beans.TestBean;
38-
3938
/**
4039
* Integration tests for {@link ImportResource} support.
4140
*
4241
* @author Chris Beams
42+
* @author Juergen Hoeller
4343
*/
4444
public class ImportResourceTests {
4545
@Test
4646
public void testImportXml() {
4747
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportXmlConfig.class);
4848
assertTrue("did not contain java-declared bean", ctx.containsBean("javaDeclaredBean"));
4949
assertTrue("did not contain xml-declared bean", ctx.containsBean("xmlDeclaredBean"));
50+
TestBean tb = ctx.getBean("javaDeclaredBean", TestBean.class);
51+
assertEquals("myName", tb.getName());
5052
}
5153

5254
@Configuration
5355
@ImportResource("classpath:org/springframework/context/annotation/configuration/ImportXmlConfig-context.xml")
5456
static class ImportXmlConfig {
57+
@Value("${name}")
58+
private String name;
5559
public @Bean TestBean javaDeclaredBean() {
56-
return new TestBean("java.declared");
60+
return new TestBean(this.name);
5761
}
5862
}
5963

@@ -63,6 +67,8 @@ public void testImportXmlWithRelativePath() {
6367
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportXmlWithRelativePathConfig.class);
6468
assertTrue("did not contain java-declared bean", ctx.containsBean("javaDeclaredBean"));
6569
assertTrue("did not contain xml-declared bean", ctx.containsBean("xmlDeclaredBean"));
70+
TestBean tb = ctx.getBean("javaDeclaredBean", TestBean.class);
71+
assertEquals("myName", tb.getName());
6672
}
6773

6874
@Configuration

0 commit comments

Comments
 (0)