提交 fd42a65c 编写于 作者: C Chris Beams

Allow ConfigurationCPP to process multiple registries

Prior to this change, an instance of ConfigurationClassPostProcessor
would throw IllegalStateException if its
postProcessBeanDefinitionRegistry method were called more than once.
This check is important to ensure that @Configuration classes are
not proxied by CGLIB multiple times, and works for most normal use
cases.

However, if the same CCPP instance is used to process multiple
registries/factories/contexts, this check creates a false negative
because it does not distinguish between invocations of
postProcessBeanDefinitionRegistry across different registries.

A use case for this, though admittedly uncommon, would be creating
a CCPP instance and registering it via
ConfigurableApplicationContext#addBeanDefinitionPostProcessor against
several ApplicationContexts. In such a case, the same CCPP instance
will post-process multiple different registry instances, and throw the
above mentioned exception.

With this change, CCPP now performs lightweight tracking of the
registries/beanFactories that it has already processed by recording
the identity hashcodes of these objects.  This is only slightly more
complex than the previous boolean-based 'already processed' flags, and
prevents this issue (however rare it may be) from occurring.

Issue: SPR-8527
上级 c5463a2e
......@@ -17,6 +17,7 @@
package org.springframework.context.annotation;
import java.io.IOException;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
......@@ -102,9 +103,9 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
private boolean setMetadataReaderFactoryCalled = false;
private boolean postProcessBeanDefinitionRegistryCalled = false;
private final Set<Integer> registriesPostProcessed = new HashSet<Integer>();
private boolean postProcessBeanFactoryCalled = false;
private final Set<Integer> factoriesPostProcessed = new HashSet<Integer>();
private Environment environment;
......@@ -163,15 +164,16 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
*/
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
BeanDefinitionReaderUtils.registerWithGeneratedName(new RootBeanDefinition(ImportAwareBeanPostProcessor.class), registry);
if (this.postProcessBeanDefinitionRegistryCalled) {
int registryID = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryID)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called for this post-processor");
"postProcessBeanDefinitionRegistry already called for this post-processor against " + registry);
}
if (this.postProcessBeanFactoryCalled) {
if (this.factoriesPostProcessed.contains(registryID)) {
throw new IllegalStateException(
"postProcessBeanFactory already called for this post-processor");
"postProcessBeanFactory already called for this post-processor against " + registry);
}
this.postProcessBeanDefinitionRegistryCalled = true;
this.registriesPostProcessed.add(registryID);
processConfigurationClasses(registry);
}
......@@ -180,12 +182,13 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
* by replacing them with CGLIB-enhanced subclasses.
*/
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
if (this.postProcessBeanFactoryCalled) {
int factoryID = System.identityHashCode(beanFactory);
if (this.factoriesPostProcessed.contains(factoryID)) {
throw new IllegalStateException(
"postProcessBeanFactory already called for this post-processor");
"postProcessBeanFactory already called for this post-processor against " + beanFactory);
}
this.postProcessBeanFactoryCalled = true;
if (!this.postProcessBeanDefinitionRegistryCalled) {
this.factoriesPostProcessed.add(factoryID);
if (!this.registriesPostProcessed.contains(factoryID)) {
// BeanDefinitionRegistryPostProcessor hook apparently not supported...
// Simply call processConfigurationClasses lazily at this point then.
processConfigurationClasses((BeanDefinitionRegistry)beanFactory);
......
......@@ -82,6 +82,25 @@ public class ConfigurationClassPostProcessorTests {
assertSame(foo, bar.foo);
}
@Test
public void testProcessingAllowedOnlyOncePerProcessorRegistryPair() {
DefaultListableBeanFactory bf1 = new DefaultListableBeanFactory();
DefaultListableBeanFactory bf2 = new DefaultListableBeanFactory();
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(bf1); // first invocation -- should succeed
try {
pp.postProcessBeanFactory(bf1); // second invocation for bf1 -- should throw
fail("expected exception");
} catch (IllegalStateException ex) {
}
pp.postProcessBeanFactory(bf2); // first invocation for bf2 -- should succeed
try {
pp.postProcessBeanFactory(bf2); // second invocation for bf2 -- should throw
fail("expected exception");
} catch (IllegalStateException ex) {
}
}
@Configuration
static class SingletonBeanConfig {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册