Andy Wilkinson opened SPR-16217 and commented
ConfigurationClassParser populates its knownSuperclasses map during the configuration phase of condition evaluation. This means that a subclass that has a register bean phase condition can cause the superclass to be skipped even if another superclass that's encountered later would have included it.
The above is perhaps best illustrated by some tests:
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.ConfigurationCondition;
import org.springframework.context.annotation.Import;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class KnownSuperclassesBug {
@Test
public void baseConfigurationIsIncludedWhenFirstSuperclassReferenceIsSkippedInRegisterBeanPhase() {
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
RegisterBeanPhaseImportingConfiguration.class)) {
context.getBean("someBean");
}
}
@Test
public void baseConfigurationIsIncludedWhenFirstSuperclassReferenceIsSkippedInParseConfigurationPhase() {
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
ParseConfigurationPhaseImportingConfiguration.class)) {
context.getBean("someBean");
}
}
public static class RegisterBeanPhaseCondition implements ConfigurationCondition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return false;
}
@Override
public ConfigurationPhase getConfigurationPhase() {
return ConfigurationPhase.REGISTER_BEAN;
}
}
public static class ParseConfigurationPhaseCondition
implements ConfigurationCondition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return false;
}
@Override
public ConfigurationPhase getConfigurationPhase() {
return ConfigurationPhase.PARSE_CONFIGURATION;
}
}
@Import({ RegisterBeanPhaseConditionConfiguration.class, BarConfiguration.class })
public static class RegisterBeanPhaseImportingConfiguration {
}
@Import({ ParseConfigurationPhaseConditionConfiguration.class,
BarConfiguration.class })
public static class ParseConfigurationPhaseImportingConfiguration {
}
public static class BaseConfiguration {
@Bean
public String someBean() {
return "foo";
}
}
@Conditional(RegisterBeanPhaseCondition.class)
public static class RegisterBeanPhaseConditionConfiguration
extends BaseConfiguration {
}
@Conditional(ParseConfigurationPhaseCondition.class)
public static class ParseConfigurationPhaseConditionConfiguration
extends BaseConfiguration {
}
public static class BarConfiguration extends BaseConfiguration {
}
}
My expectation is that both tests will pass. As things stand baseConfigurationIsIncludedWhenFirstSuperclassReferenceIsSkippedInRegisterBeanPhase fails and baseConfigurationIsIncludedWhenFirstSuperclassReferenceIsSkippedInParseConfigurationPhase() passes.
The first test fails because RegisterBeanPhaseConditionConfiguration is processed and BaseConfiguration is added to the knownSuperclasses map. When BarConfiguration is processed BaseConfiguration is already in the knownSuperclasses map so its "import" via BarConfiguration is lost. When the register bean phase condition on RegisterBeanPhaseConditionConfiguration is evaluated it doesn't match so it's skipped along with BaseConfiguration that it imports.
Affects: 4.3.12, 5.0.1
Reference URL: spring-projects/spring-boot#11063
Issue Links:
Referenced from: commits 47383fc, 08c95fb
1 votes, 5 watchers
Andy Wilkinson opened SPR-16217 and commented
ConfigurationClassParserpopulates itsknownSuperclassesmap during the configuration phase of condition evaluation. This means that a subclass that has a register bean phase condition can cause the superclass to be skipped even if another superclass that's encountered later would have included it.The above is perhaps best illustrated by some tests:
My expectation is that both tests will pass. As things stand
baseConfigurationIsIncludedWhenFirstSuperclassReferenceIsSkippedInRegisterBeanPhasefails andbaseConfigurationIsIncludedWhenFirstSuperclassReferenceIsSkippedInParseConfigurationPhase()passes.The first test fails because
RegisterBeanPhaseConditionConfigurationis processed andBaseConfigurationis added to theknownSuperclassesmap. WhenBarConfigurationis processedBaseConfigurationis already in theknownSuperclassesmap so its "import" viaBarConfigurationis lost. When the register bean phase condition onRegisterBeanPhaseConditionConfigurationis evaluated it doesn't match so it's skipped along withBaseConfigurationthat it imports.Affects: 4.3.12, 5.0.1
Reference URL: spring-projects/spring-boot#11063
Issue Links:
Referenced from: commits 47383fc, 08c95fb
1 votes, 5 watchers