Skip to content

Commit 540d0d9

Browse files
committed
Avoid Autowired shortcut resolution for NullBean values
Includes getBean documentation against NullBean values. Closes gh-30485 (cherry picked from commit 8b8d147)
1 parent 572bbee commit 540d0d9

File tree

3 files changed

+34
-7
lines changed

3 files changed

+34
-7
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -136,7 +136,10 @@ public interface BeanFactory {
136136
* <p>Translates aliases back to the corresponding canonical bean name.
137137
* <p>Will ask the parent factory if the bean cannot be found in this factory instance.
138138
* @param name the name of the bean to retrieve
139-
* @return an instance of the bean
139+
* @return an instance of the bean.
140+
* Note that the return value will never be {@code null} but possibly a stub for
141+
* {@code null} returned from a factory method, to be checked via {@code equals(null)}.
142+
* Consider using {@link #getBeanProvider(Class)} for resolving optional dependencies.
140143
* @throws NoSuchBeanDefinitionException if there is no bean with the specified name
141144
* @throws BeansException if the bean could not be obtained
142145
*/
@@ -152,7 +155,11 @@ public interface BeanFactory {
152155
* <p>Will ask the parent factory if the bean cannot be found in this factory instance.
153156
* @param name the name of the bean to retrieve
154157
* @param requiredType type the bean must match; can be an interface or superclass
155-
* @return an instance of the bean
158+
* @return an instance of the bean.
159+
* Note that the return value will never be {@code null}. In case of a stub for
160+
* {@code null} from a factory method having been resolved for the requested bean, a
161+
* {@code BeanNotOfRequiredTypeException} against the NullBean stub will be raised.
162+
* Consider using {@link #getBeanProvider(Class)} for resolving optional dependencies.
156163
* @throws NoSuchBeanDefinitionException if there is no such bean definition
157164
* @throws BeanNotOfRequiredTypeException if the bean is not of the required type
158165
* @throws BeansException if the bean could not be created

spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -665,7 +665,7 @@ private Object resolveFieldValue(Field field, Object bean, @Nullable String bean
665665
if (value != null || this.required) {
666666
cachedFieldValue = desc;
667667
registerDependentBeans(beanName, autowiredBeanNames);
668-
if (autowiredBeanNames.size() == 1) {
668+
if (value != null && autowiredBeanNames.size() == 1) {
669669
String autowiredBeanName = autowiredBeanNames.iterator().next();
670670
if (beanFactory.containsBean(autowiredBeanName) &&
671671
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
@@ -778,7 +778,7 @@ private Object[] resolveMethodArguments(Method method, Object bean, @Nullable St
778778
Class<?>[] paramTypes = method.getParameterTypes();
779779
for (int i = 0; i < paramTypes.length; i++) {
780780
String autowiredBeanName = it.next();
781-
if (beanFactory.containsBean(autowiredBeanName) &&
781+
if (arguments[i] != null && beanFactory.containsBean(autowiredBeanName) &&
782782
beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
783783
cachedMethodArguments[i] = new ShortcutDependencyDescriptor(
784784
descriptors[i], autowiredBeanName, paramTypes[i]);

spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -133,6 +133,26 @@ public void testResourceInjection() {
133133
assertThat(bean.getTestBean2()).isSameAs(tb);
134134
}
135135

136+
@Test
137+
public void testResourceInjectionWithNullBean() {
138+
RootBeanDefinition bd = new RootBeanDefinition(NonPublicResourceInjectionBean.class);
139+
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
140+
bf.registerBeanDefinition("annotatedBean", bd);
141+
RootBeanDefinition tb = new RootBeanDefinition(NullFactoryMethods.class);
142+
tb.setFactoryMethodName("createTestBean");
143+
bf.registerBeanDefinition("testBean", tb);
144+
145+
NonPublicResourceInjectionBean bean = (NonPublicResourceInjectionBean) bf.getBean("annotatedBean");
146+
assertThat(bean.getTestBean()).isNull();
147+
assertThat(bean.getTestBean2()).isNull();
148+
assertThat(bean.getTestBean3()).isNull();
149+
150+
bean = (NonPublicResourceInjectionBean) bf.getBean("annotatedBean");
151+
assertThat(bean.getTestBean()).isNull();
152+
assertThat(bean.getTestBean2()).isNull();
153+
assertThat(bean.getTestBean3()).isNull();
154+
}
155+
136156
@Test
137157
public void testExtendedResourceInjection() {
138158
RootBeanDefinition bd = new RootBeanDefinition(TypedExtendedResourceInjectionBean.class);

0 commit comments

Comments
 (0)