Skip to content

Commit b5d153f

Browse files
committed
Enforce consistent null check on injection points for pre-existing singleton beans
Includes consistent JSpecify nullness check on fields as optional injection points Closes gh-34952 See gh-34261
1 parent cdfe089 commit b5d153f

File tree

3 files changed

+23
-23
lines changed

3 files changed

+23
-23
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
3535
import org.springframework.core.KotlinDetector;
3636
import org.springframework.core.MethodParameter;
37+
import org.springframework.core.Nullness;
3738
import org.springframework.core.ParameterNameDiscoverer;
3839
import org.springframework.core.ResolvableType;
3940
import org.springframework.core.convert.TypeDescriptor;
@@ -162,7 +163,7 @@ public boolean isRequired() {
162163
}
163164

164165
if (this.field != null) {
165-
return !(this.field.getType() == Optional.class || hasNullableAnnotation() ||
166+
return !(this.field.getType() == Optional.class || Nullness.forField(this.field) == Nullness.NULLABLE ||
166167
(KotlinDetector.isKotlinType(this.field.getDeclaringClass()) && KotlinDelegate.isNullable(this.field)));
167168
}
168169
else {

spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1656,12 +1656,7 @@ else if (descriptor.supportsLazyResolution()) {
16561656
if (autowiredBeanNames != null) {
16571657
autowiredBeanNames.add(dependencyName);
16581658
}
1659-
boolean preExisting = containsSingleton(dependencyName);
16601659
Object dependencyBean = getBean(dependencyName);
1661-
if (preExisting && dependencyBean instanceof NullBean) {
1662-
// for backwards compatibility with addCandidateEntry in the regular code path
1663-
dependencyBean = null;
1664-
}
16651660
return resolveInstance(dependencyBean, descriptor, type, dependencyName);
16661661
}
16671662
}
@@ -1972,7 +1967,7 @@ private void addCandidateEntry(Map<String, Object> candidates, String candidateN
19721967
else if (containsSingleton(candidateName) ||
19731968
(descriptor instanceof StreamDependencyDescriptor streamDescriptor && streamDescriptor.isOrdered())) {
19741969
Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this);
1975-
candidates.put(candidateName, (beanInstance instanceof NullBean ? null : beanInstance));
1970+
candidates.put(candidateName, beanInstance);
19761971
}
19771972
else {
19781973
candidates.put(candidateName, getType(candidateName));

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

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -974,7 +974,9 @@ void constructorResourceInjectionWithCandidateAndNoFallback() {
974974
bf.registerBeanDefinition("testBean", tb);
975975

976976
bf.getBean("testBean");
977-
assertThat(bf.getBean("annotatedBean", ConstructorWithoutFallbackBean.class).getTestBean3()).isNull();
977+
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
978+
.isThrownBy(() -> bf.getBean("annotatedBean"))
979+
.satisfies(methodParameterDeclaredOn(ConstructorWithoutFallbackBean.class));
978980
}
979981

980982
@Test
@@ -985,7 +987,9 @@ void constructorResourceInjectionWithNameMatchingCandidateAndNoFallback() {
985987
bf.registerBeanDefinition("testBean3", tb);
986988

987989
bf.getBean("testBean3");
988-
assertThat(bf.getBean("annotatedBean", ConstructorWithoutFallbackBean.class).getTestBean3()).isNull();
990+
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
991+
.isThrownBy(() -> bf.getBean("annotatedBean"))
992+
.satisfies(methodParameterDeclaredOn(ConstructorWithoutFallbackBean.class));
989993
}
990994

991995
@Test
@@ -2733,22 +2737,22 @@ private void testBeanQualifierProvider() {}
27332737
public static class ResourceInjectionBean {
27342738

27352739
@Autowired(required = false)
2736-
private TestBean testBean;
2740+
private @Nullable TestBean testBean;
27372741

2738-
TestBean testBean2;
2742+
@Nullable TestBean testBean2;
27392743

27402744
@Autowired
2741-
public void setTestBean2(TestBean testBean2) {
2745+
public void setTestBean2(@Nullable TestBean testBean2) {
27422746
Assert.state(this.testBean != null, "Wrong initialization order");
27432747
Assert.state(this.testBean2 == null, "Already called");
27442748
this.testBean2 = testBean2;
27452749
}
27462750

2747-
public TestBean getTestBean() {
2751+
public @Nullable TestBean getTestBean() {
27482752
return this.testBean;
27492753
}
27502754

2751-
public TestBean getTestBean2() {
2755+
public @Nullable TestBean getTestBean2() {
27522756
return this.testBean2;
27532757
}
27542758
}
@@ -2757,13 +2761,13 @@ public TestBean getTestBean2() {
27572761
static class NonPublicResourceInjectionBean<T> extends ResourceInjectionBean {
27582762

27592763
@Autowired
2760-
public final ITestBean testBean3 = null;
2764+
public final @Nullable ITestBean testBean3 = null;
27612765

2762-
private T nestedTestBean;
2766+
private @Nullable T nestedTestBean;
27632767

2764-
private ITestBean testBean4;
2768+
private @Nullable ITestBean testBean4;
27652769

2766-
protected BeanFactory beanFactory;
2770+
protected @Nullable BeanFactory beanFactory;
27672771

27682772
public boolean baseInjected = false;
27692773

@@ -2772,18 +2776,18 @@ public NonPublicResourceInjectionBean() {
27722776

27732777
@Override
27742778
@Autowired
2775-
public void setTestBean2(TestBean testBean2) {
2779+
public void setTestBean2(@Nullable TestBean testBean2) {
27762780
this.testBean2 = testBean2;
27772781
}
27782782

27792783
@Autowired
2780-
private void inject(ITestBean testBean4, T nestedTestBean) {
2784+
private void inject(@Nullable ITestBean testBean4, @Nullable T nestedTestBean) {
27812785
this.testBean4 = testBean4;
27822786
this.nestedTestBean = nestedTestBean;
27832787
}
27842788

27852789
@Autowired
2786-
private void inject(ITestBean testBean4) {
2790+
private void inject(@Nullable ITestBean testBean4) {
27872791
this.baseInjected = true;
27882792
}
27892793

@@ -2793,11 +2797,11 @@ protected void initBeanFactory(BeanFactory beanFactory) {
27932797
this.beanFactory = beanFactory;
27942798
}
27952799

2796-
public ITestBean getTestBean3() {
2800+
public @Nullable ITestBean getTestBean3() {
27972801
return this.testBean3;
27982802
}
27992803

2800-
public ITestBean getTestBean4() {
2804+
public @Nullable ITestBean getTestBean4() {
28012805
return this.testBean4;
28022806
}
28032807

0 commit comments

Comments
 (0)