Browse Source

Improve exception handling in ConfigurationClassBeanDefinitionReader

Thanks to a proposal from @wilkinsona, this commit introduces a
try-catch block in loadBeanDefinitions(...) which throws an
IllegalStateException that provides context regarding the configuration
class and cause of the failure.

Closes gh-35631

Co-authored-by: Andy Wilkinson <andy.wilkinson@broadcom.com>
pull/35672/head
Sam Brannen 2 months ago
parent
commit
01acb80501
  1. 8
      spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java
  2. 36
      spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java
  3. 9
      spring-context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java

8
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java

@ -117,7 +117,13 @@ class ConfigurationClassBeanDefinitionReader { @@ -117,7 +117,13 @@ class ConfigurationClassBeanDefinitionReader {
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
try {
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
catch (Exception ex) {
throw new IllegalStateException("Failed to load bean definitions for configuration class '" +
configClass.getMetadata().getClassName() + "'", ex);
}
}
}

36
spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java

@ -409,23 +409,36 @@ class ConfigurationClassPostProcessorTests { @@ -409,23 +409,36 @@ class ConfigurationClassPostProcessorTests {
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(SingletonBeanConfig.class));
beanFactory.setAllowBeanDefinitionOverriding(false);
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
assertThatExceptionOfType(BeanDefinitionStoreException.class)
assertThatIllegalStateException()
.isThrownBy(() -> pp.postProcessBeanFactory(beanFactory))
.withMessageContaining("bar")
.withMessageContaining("SingletonBeanConfig")
.withMessageContaining(TestBean.class.getName());
.withMessage("Failed to load bean definitions for configuration class '%s'", SingletonBeanConfig.class.getName())
.havingCause()
.isInstanceOf(BeanDefinitionStoreException.class)
.withMessageContainingAll(
"bar",
"SingletonBeanConfig",
TestBean.class.getName()
);
}
@Test // gh-25430
void detectAliasOverride() {
Class<?> configClass = SecondConfiguration.class;
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
beanFactory.setAllowBeanDefinitionOverriding(false);
context.register(FirstConfiguration.class, SecondConfiguration.class);
context.register(FirstConfiguration.class, configClass);
assertThatIllegalStateException().isThrownBy(context::refresh)
.withMessageContaining("alias 'taskExecutor'")
.withMessageContaining("name 'applicationTaskExecutor'")
.withMessageContaining("bean definition 'taskExecutor'");
.withMessage("Failed to load bean definitions for configuration class '%s'", configClass.getName())
.havingCause()
.isExactlyInstanceOf(IllegalStateException.class)
.withMessageContainingAll(
"alias 'taskExecutor'",
"name 'applicationTaskExecutor'",
"bean definition 'taskExecutor'"
);
context.close();
}
@ -1158,8 +1171,11 @@ class ConfigurationClassPostProcessorTests { @@ -1158,8 +1171,11 @@ class ConfigurationClassPostProcessorTests {
@Test
void testNameClashBetweenConfigurationClassAndBean() {
assertThatExceptionOfType(BeanDefinitionStoreException.class)
.isThrownBy(() -> new AnnotationConfigApplicationContext(MyTestBean.class).getBean("myTestBean", TestBean.class));
assertThatIllegalStateException()
.isThrownBy(() -> new AnnotationConfigApplicationContext(MyTestBean.class))
.withMessage("Failed to load bean definitions for configuration class '%s'", MyTestBean.class.getName())
.havingCause()
.isInstanceOf(BeanDefinitionStoreException.class);
}
@Test

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

@ -60,6 +60,7 @@ import org.springframework.context.support.GenericApplicationContext; @@ -60,6 +60,7 @@ import org.springframework.context.support.GenericApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
/**
* Miscellaneous system tests covering {@link Bean} naming, aliases, scoping and
@ -222,8 +223,12 @@ class ConfigurationClassProcessingTests { @@ -222,8 +223,12 @@ class ConfigurationClassProcessingTests {
@Test // gh-33330
void configurationWithMethodNameMismatch() {
assertThatExceptionOfType(BeanDefinitionOverrideException.class)
.isThrownBy(() -> initBeanFactory(false, ConfigWithMethodNameMismatch.class));
Class<?> configClass = ConfigWithMethodNameMismatch.class;
assertThatIllegalStateException()
.isThrownBy(() -> initBeanFactory(false, configClass))
.withMessage("Failed to load bean definitions for configuration class '%s'", configClass.getName())
.havingCause()
.isInstanceOf(BeanDefinitionOverrideException.class);
}
@Test // gh-33920

Loading…
Cancel
Save