Make @Configuration class enhancement idempotent

The registration of more than one ConfigurationClassPostProcessor
results in the double-enhancement of @Configuration classes, i.e. a
two-deep CGLIB subclass hierarchy is created.

As a side-effect of changes introduced in 3.1 M2 fixing SPR-8080, this
behavior now results in an infinite loop at CGLIB callback processing
time, leading to a StackOverflowException which is then suppressed by
the container, and ultimately results in the user being presented with
an unintuitive "Bean 'x' is not already in creation" exception.

This fix introduces a marker interface 'EnhancedConfiguration' to be
implemented by all generated @Configuration subclasses. The
configuration class enhancer can then behave in an idempotent fashion
by checking to see whether a candidate @Configuration class is already
assignable to this type i.e. already enhanced and ignore it if so.

Naturally, users should avoid registering more than one
ConfigurationClassPostProcessor, but this is not always possible. As
with the case in point, SPR-8824 originates from problems with
spring-data-neo4j, which explicitly registers its own
ConfigurationClassPostProcessor. The user has little control over this
arrangement, so it is important that the framework is defensive as
described above.

Issue: SPR-8824
This commit is contained in:
Chris Beams
2011-11-18 03:25:16 +00:00
parent 01cc76f8e3
commit 224cf11fcb
4 changed files with 90 additions and 10 deletions

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:neo4j="http://www.springframework.org/schema/data/neo4j"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/data/neo4j http://www.springframework.org/schema/data/neo4j/spring-neo4j-2.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<bean id="a" class="org.springframework.context.annotation.ConfigurationClassPostProcessor"/>
<bean id="b" class="org.springframework.context.annotation.ConfigurationClassPostProcessor"/>
<bean id="c" class="org.springframework.context.annotation.DuplicateConfigurationClassPostProcessorTests$Config"/>
</beans>

View File

@@ -0,0 +1,37 @@
package org.springframework.context.annotation.configuration;
import org.junit.Test;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
/**
* Corners the bug originally reported by SPR-8824, where the presence of two
* {@link ConfigurationClassPostProcessor} beans in combination with a @Configuration
* class having at least one @Bean method causes a "Singleton 'foo' isn't currently in
* creation" exception.
*
* @author Chris Beams
* @since 3.1
*/
public class DuplicateConfigurationClassPostProcessorTests {
@Test
public void test() {
GenericApplicationContext ctx = new GenericApplicationContext();
ctx.registerBeanDefinition("a", new RootBeanDefinition(ConfigurationClassPostProcessor.class));
ctx.registerBeanDefinition("b", new RootBeanDefinition(ConfigurationClassPostProcessor.class));
ctx.registerBeanDefinition("myConfig", new RootBeanDefinition(Config.class));
ctx.refresh();
}
@Configuration
static class Config {
@Bean
public String string() {
return "bean";
}
}
}