Skip to content

Commit 30bd582

Browse files
committed
ConfigurationClassEnhancer leniently allows for null bean references
Issue: SPR-15829
1 parent 204ddeb commit 30bd582

File tree

2 files changed

+44
-12
lines changed

2 files changed

+44
-12
lines changed

spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ private static class BeanMethodInterceptor implements MethodInterceptor, Conditi
309309
* super implementation of the proxied method i.e., the actual {@code @Bean} method
310310
*/
311311
@Override
312+
@Nullable
312313
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
313314
MethodProxy cglibMethodProxy) throws Throwable {
314315

@@ -360,10 +361,11 @@ public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object
360361
return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
361362
}
362363

363-
return obtainBeanInstanceFromFactory(beanMethod, beanMethodArgs, beanFactory, beanName);
364+
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
364365
}
365366

366-
private Object obtainBeanInstanceFromFactory(Method beanMethod, Object[] beanMethodArgs,
367+
@Nullable
368+
private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs,
367369
ConfigurableBeanFactory beanFactory, String beanName) {
368370

369371
// The user (i.e. not the factory) is requesting this bean through a call to
@@ -390,18 +392,29 @@ private Object obtainBeanInstanceFromFactory(Method beanMethod, Object[] beanMet
390392
Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) :
391393
beanFactory.getBean(beanName));
392394
if (!ClassUtils.isAssignableValue(beanMethod.getReturnType(), beanInstance)) {
393-
String msg = String.format("@Bean method %s.%s called as a bean reference " +
395+
if (beanInstance.equals(null)) {
396+
if (logger.isDebugEnabled()) {
397+
logger.debug(String.format("@Bean method %s.%s called as bean reference " +
398+
"for type [%s] returned null bean; resolving to null value.",
399+
beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName(),
400+
beanMethod.getReturnType().getName()));
401+
}
402+
beanInstance = null;
403+
}
404+
else {
405+
String msg = String.format("@Bean method %s.%s called as bean reference " +
394406
"for type [%s] but overridden by non-compatible bean instance of type [%s].",
395407
beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName(),
396408
beanMethod.getReturnType().getName(), beanInstance.getClass().getName());
397-
try {
398-
BeanDefinition beanDefinition = beanFactory.getMergedBeanDefinition(beanName);
399-
msg += " Overriding bean of same name declared in: " + beanDefinition.getResourceDescription();
400-
}
401-
catch (NoSuchBeanDefinitionException ex) {
402-
// Ignore - simply no detailed message then.
409+
try {
410+
BeanDefinition beanDefinition = beanFactory.getMergedBeanDefinition(beanName);
411+
msg += " Overriding bean of same name declared in: " + beanDefinition.getResourceDescription();
412+
}
413+
catch (NoSuchBeanDefinitionException ex) {
414+
// Ignore - simply no detailed message then.
415+
}
416+
throw new IllegalStateException(msg);
403417
}
404-
throw new IllegalStateException(msg);
405418
}
406419
Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod();
407420
if (currentlyInvoked != null) {

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

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,15 @@ public void configurationWithPrototypeScopedBeans() {
205205
assertNotSame(bar.getSpouse(), baz);
206206
}
207207

208+
@Test
209+
public void configurationWithNullReference() {
210+
BeanFactory factory = initBeanFactory(ConfigWithNullReference.class);
211+
212+
TestBean foo = factory.getBean("foo", TestBean.class);
213+
assertTrue(factory.getBean("bar").equals(null));
214+
assertNull(foo.getSpouse());
215+
}
216+
208217
@Test
209218
public void configurationWithAdaptivePrototypes() {
210219
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
@@ -307,7 +316,7 @@ static class ConfigWithBeanWithAliases {
307316

308317
static TestBean testBean = new TestBean(ConfigWithBeanWithAliases.class.getSimpleName());
309318

310-
@Bean(name = { "name1", "alias1", "alias2", "alias3" })
319+
@Bean(name = {"name1", "alias1", "alias2", "alias3"})
311320
public TestBean methodName() {
312321
return testBean;
313322
}
@@ -319,7 +328,7 @@ static class ConfigWithBeanWithAliasesConfiguredViaValueAttribute {
319328

320329
static TestBean testBean = new TestBean(ConfigWithBeanWithAliasesConfiguredViaValueAttribute.class.getSimpleName());
321330

322-
@Bean({ "enigma", "alias1", "alias2", "alias3" })
331+
@Bean({"enigma", "alias1", "alias2", "alias3"})
323332
public TestBean methodName() {
324333
return testBean;
325334
}
@@ -415,6 +424,16 @@ public TestBean adaptive2(DependencyDescriptor dd) {
415424
}
416425

417426

427+
@Configuration
428+
static class ConfigWithNullReference extends ConfigWithPrototypeBean {
429+
430+
@Override
431+
public TestBean bar() {
432+
return null;
433+
}
434+
}
435+
436+
418437
@Scope("prototype")
419438
static class AdaptiveInjectionPoints {
420439

0 commit comments

Comments
 (0)