Skip to content

Commit acdd8a0

Browse files
committed
Improve exception handling in bean override support
- Do not silently abort bean override processing if the ApplicationContext is not a BeanDefinitionRegistry. - Include conflicting bean names in error messages instead of just the number of conflicting beans. - Consistently throw IllegalStateException. - etc.
1 parent 7a11899 commit acdd8a0

File tree

7 files changed

+48
-39
lines changed

7 files changed

+48
-39
lines changed

spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessor.java

+20-18
Original file line numberDiff line numberDiff line change
@@ -130,15 +130,16 @@ private void registerReplaceDefinition(ConfigurableListableBeanFactory beanFacto
130130
String beanName = overrideMetadata.getBeanName();
131131
BeanDefinition existingBeanDefinition = null;
132132
if (beanName == null) {
133-
Set<String> candidates = getExistingBeanNamesByType(beanFactory, overrideMetadata, true);
134-
if (candidates.size() != 1) {
135-
Field f = overrideMetadata.getField();
136-
throw new IllegalStateException("Unable to select a bean definition to override: " +
137-
candidates.size() + " bean definitions found of type " + overrideMetadata.getBeanType() +
138-
" (as required by annotated field '" + f.getDeclaringClass().getSimpleName() +
139-
"." + f.getName() + "')");
133+
Set<String> candidateNames = getExistingBeanNamesByType(beanFactory, overrideMetadata, true);
134+
int candidateCount = candidateNames.size();
135+
if (candidateCount != 1) {
136+
Field field = overrideMetadata.getField();
137+
throw new IllegalStateException("Unable to select a bean definition to override: found " +
138+
candidateCount + " bean definitions of type " + overrideMetadata.getBeanType() +
139+
" (as required by annotated field '" + field.getDeclaringClass().getSimpleName() +
140+
"." + field.getName() + "')" + (candidateCount > 0 ? ": " + candidateNames : ""));
140141
}
141-
beanName = candidates.iterator().next();
142+
beanName = candidateNames.iterator().next();
142143
existingBeanDefinition = beanFactory.getBeanDefinition(beanName);
143144
}
144145
else {
@@ -147,8 +148,8 @@ private void registerReplaceDefinition(ConfigurableListableBeanFactory beanFacto
147148
existingBeanDefinition = beanFactory.getBeanDefinition(beanName);
148149
}
149150
else if (enforceExistingDefinition) {
150-
throw new IllegalStateException("Unable to override bean '" + beanName + "': there is no" +
151-
" bean definition to replace with that name of type " + overrideMetadata.getBeanType());
151+
throw new IllegalStateException("Unable to override bean '" + beanName + "': there is no " +
152+
"bean definition to replace with that name of type " + overrideMetadata.getBeanType());
152153
}
153154
}
154155

@@ -181,20 +182,21 @@ private void registerWrapBean(ConfigurableListableBeanFactory beanFactory, Overr
181182
String beanName = metadata.getBeanName();
182183
if (beanName == null) {
183184
Set<String> candidateNames = getExistingBeanNamesByType(beanFactory, metadata, true);
184-
if (candidateNames.size() != 1) {
185-
Field f = metadata.getField();
186-
throw new IllegalStateException("Unable to select a bean to override by wrapping: " +
187-
candidateNames.size() + " bean instances found of type " + metadata.getBeanType() +
188-
" (as required by annotated field '" + f.getDeclaringClass().getSimpleName() +
189-
"." + f.getName() + "')");
185+
int candidateCount = candidateNames.size();
186+
if (candidateCount != 1) {
187+
Field field = metadata.getField();
188+
throw new IllegalStateException("Unable to select a bean to override by wrapping: found " +
189+
candidateCount + " bean instances of type " + metadata.getBeanType() +
190+
" (as required by annotated field '" + field.getDeclaringClass().getSimpleName() +
191+
"." + field.getName() + "')" + (candidateCount > 0 ? ": " + candidateNames : ""));
190192
}
191193
beanName = candidateNames.iterator().next();
192194
}
193195
else {
194196
Set<String> candidates = getExistingBeanNamesByType(beanFactory, metadata, false);
195197
if (!candidates.contains(beanName)) {
196-
throw new IllegalStateException("Unable to override bean '" + beanName + "' by wrapping: there is no" +
197-
" existing bean instance with that name of type " + metadata.getBeanType());
198+
throw new IllegalStateException("Unable to override bean '" + beanName + "' by wrapping: there is no " +
199+
"existing bean instance with that name of type " + metadata.getBeanType());
198200
}
199201
}
200202
this.overrideRegistrar.markWrapEarly(metadata, beanName);

spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideContextCustomizer.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,11 @@ private static void addInfrastructureBeanDefinition(BeanDefinitionRegistry regis
8181

8282
@Override
8383
public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
84-
if (context instanceof BeanDefinitionRegistry registry) {
85-
registerInfrastructure(registry, this.detectedClasses);
84+
if (!(context instanceof BeanDefinitionRegistry registry)) {
85+
throw new IllegalStateException("Cannot process bean overrides with an ApplicationContext " +
86+
"that doesn't implement BeanDefinitionRegistry: " + context.getClass());
8687
}
88+
registerInfrastructure(registry, this.detectedClasses);
8789
}
8890

8991
@Override

spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBeanOverrideProcessor.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,8 @@ static Method findTestBeanFactoryMethod(Class<?> clazz, Class<?> methodReturnTyp
111111
@Override
112112
public TestBeanOverrideMetadata createMetadata(Annotation overrideAnnotation, Class<?> testClass, Field field) {
113113
if (!(overrideAnnotation instanceof TestBean testBeanAnnotation)) {
114-
throw new IllegalStateException(String.format("Invalid annotation passed to %s: expected @TestBean on field %s.%s",
115-
TestBeanOverrideProcessor.class.getSimpleName(), field.getDeclaringClass().getName(),
116-
field.getName()));
114+
throw new IllegalStateException("Invalid annotation passed to %s: expected @TestBean on field %s.%s"
115+
.formatted(getClass().getSimpleName(), field.getDeclaringClass().getName(), field.getName()));
117116
}
118117
Method overrideMethod;
119118
String methodName = testBeanAnnotation.methodName();
@@ -172,7 +171,7 @@ protected Object createOverride(String beanName, @Nullable BeanDefinition existi
172171
return this.overrideMethod.invoke(null);
173172
}
174173
catch (IllegalAccessException | InvocationTargetException ex) {
175-
throw new IllegalArgumentException("Could not invoke bean overriding method " + this.overrideMethod.getName() +
174+
throw new IllegalStateException("Failed to invoke bean overriding method " + this.overrideMethod.getName() +
176175
"; a static method with no formal parameters is expected", ex);
177176
}
178177
}

spring-test/src/test/java/org/springframework/test/context/bean/override/convention/FailingTestBeanByTypeIntegrationTests.java

+7-5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.test.context.bean.override.convention;
1818

19+
import java.util.List;
20+
1921
import org.junit.jupiter.api.Test;
2022

2123
import org.springframework.context.annotation.Bean;
@@ -49,8 +51,8 @@ void zeroCandidates() {
4951
cause(
5052
instanceOf(IllegalStateException.class),
5153
message("""
52-
Unable to select a bean definition to override: 0 bean definitions \
53-
found of type %s (as required by annotated field '%s.example')"""
54+
Unable to select a bean definition to override: found 0 bean definitions \
55+
of type %s (as required by annotated field '%s.example')"""
5456
.formatted(ExampleService.class.getName(), testClass.getSimpleName())))));
5557
}
5658

@@ -62,9 +64,9 @@ void tooManyCandidates() {
6264
cause(
6365
instanceOf(IllegalStateException.class),
6466
message("""
65-
Unable to select a bean definition to override: 2 bean definitions \
66-
found of type %s (as required by annotated field '%s.example')"""
67-
.formatted(ExampleService.class.getName(), testClass.getSimpleName())))));
67+
Unable to select a bean definition to override: found 2 bean definitions \
68+
of type %s (as required by annotated field '%s.example'): %s"""
69+
.formatted(ExampleService.class.getName(), testClass.getSimpleName(), List.of("bean1", "bean2"))))));
6870
}
6971

7072

spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/FailingMockitoBeanByTypeIntegrationTests.java

+7-5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.test.context.bean.override.mockito;
1818

19+
import java.util.List;
20+
1921
import org.junit.jupiter.api.Test;
2022

2123
import org.springframework.context.annotation.Bean;
@@ -49,8 +51,8 @@ void zeroCandidates() {
4951
cause(
5052
instanceOf(IllegalStateException.class),
5153
message("""
52-
Unable to select a bean definition to override: 0 bean definitions \
53-
found of type %s (as required by annotated field '%s.example')"""
54+
Unable to select a bean definition to override: found 0 bean definitions \
55+
of type %s (as required by annotated field '%s.example')"""
5456
.formatted(ExampleService.class.getName(), testClass.getSimpleName())))));
5557
}
5658

@@ -62,9 +64,9 @@ void tooManyCandidates() {
6264
cause(
6365
instanceOf(IllegalStateException.class),
6466
message("""
65-
Unable to select a bean definition to override: 2 bean definitions \
66-
found of type %s (as required by annotated field '%s.example')"""
67-
.formatted(ExampleService.class.getName(), testClass.getSimpleName())))));
67+
Unable to select a bean definition to override: found 2 bean definitions \
68+
of type %s (as required by annotated field '%s.example'): %s"""
69+
.formatted(ExampleService.class.getName(), testClass.getSimpleName(), List.of("bean1", "bean2"))))));
6870
}
6971

7072

spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/FailingMockitoSpyBeanByTypeIntegrationTests.java

+6-4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.test.context.bean.override.mockito;
1818

19+
import java.util.List;
20+
1921
import org.junit.jupiter.api.Test;
2022

2123
import org.springframework.context.annotation.Bean;
@@ -48,7 +50,7 @@ void zeroCandidates() {
4850
cause(
4951
instanceOf(IllegalStateException.class),
5052
message("""
51-
Unable to select a bean to override by wrapping: 0 bean instances found of \
53+
Unable to select a bean to override by wrapping: found 0 bean instances of \
5254
type %s (as required by annotated field '%s.example')"""
5355
.formatted(ExampleService.class.getName(), testClass.getSimpleName())))));
5456
}
@@ -61,9 +63,9 @@ void tooManyCandidates() {
6163
cause(
6264
instanceOf(IllegalStateException.class),
6365
message("""
64-
Unable to select a bean to override by wrapping: 2 bean instances found of \
65-
type %s (as required by annotated field '%s.example')"""
66-
.formatted(ExampleService.class.getName(), testClass.getSimpleName())))));
66+
Unable to select a bean to override by wrapping: found 2 bean instances of \
67+
type %s (as required by annotated field '%s.example'): %s"""
68+
.formatted(ExampleService.class.getName(), testClass.getSimpleName(), List.of("bean1", "bean2"))))));
6769
}
6870

6971

spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/FailingMockitoSpyBeanIntegrationTests.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ void failWhenBeanNotPresentByType() {
4444
finishedWithFailure(
4545
cause(instanceOf(IllegalStateException.class),
4646
message("""
47-
Unable to select a bean to override by wrapping: 0 bean instances found \
47+
Unable to select a bean to override by wrapping: found 0 bean instances \
4848
of type %s (as required by annotated field '%s.notPresent')"""
4949
.formatted(ExampleService.class.getName(), testClass.getSimpleName())))));
5050
}

0 commit comments

Comments
 (0)