Browse Source

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
pull/34716/head
Juergen Hoeller 7 months ago
parent
commit
b5d153febf
  1. 3
      spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java
  2. 7
      spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
  3. 36
      spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java

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

@ -34,6 +34,7 @@ import org.springframework.beans.factory.InjectionPoint; @@ -34,6 +34,7 @@ import org.springframework.beans.factory.InjectionPoint;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.core.KotlinDetector;
import org.springframework.core.MethodParameter;
import org.springframework.core.Nullness;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.ResolvableType;
import org.springframework.core.convert.TypeDescriptor;
@ -162,7 +163,7 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable @@ -162,7 +163,7 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable
}
if (this.field != null) {
return !(this.field.getType() == Optional.class || hasNullableAnnotation() ||
return !(this.field.getType() == Optional.class || Nullness.forField(this.field) == Nullness.NULLABLE ||
(KotlinDetector.isKotlinType(this.field.getDeclaringClass()) && KotlinDelegate.isNullable(this.field)));
}
else {

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

@ -1656,12 +1656,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @@ -1656,12 +1656,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
if (autowiredBeanNames != null) {
autowiredBeanNames.add(dependencyName);
}
boolean preExisting = containsSingleton(dependencyName);
Object dependencyBean = getBean(dependencyName);
if (preExisting && dependencyBean instanceof NullBean) {
// for backwards compatibility with addCandidateEntry in the regular code path
dependencyBean = null;
}
return resolveInstance(dependencyBean, descriptor, type, dependencyName);
}
}
@ -1972,7 +1967,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @@ -1972,7 +1967,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
else if (containsSingleton(candidateName) ||
(descriptor instanceof StreamDependencyDescriptor streamDescriptor && streamDescriptor.isOrdered())) {
Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this);
candidates.put(candidateName, (beanInstance instanceof NullBean ? null : beanInstance));
candidates.put(candidateName, beanInstance);
}
else {
candidates.put(candidateName, getType(candidateName));

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

@ -974,7 +974,9 @@ class AutowiredAnnotationBeanPostProcessorTests { @@ -974,7 +974,9 @@ class AutowiredAnnotationBeanPostProcessorTests {
bf.registerBeanDefinition("testBean", tb);
bf.getBean("testBean");
assertThat(bf.getBean("annotatedBean", ConstructorWithoutFallbackBean.class).getTestBean3()).isNull();
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
.isThrownBy(() -> bf.getBean("annotatedBean"))
.satisfies(methodParameterDeclaredOn(ConstructorWithoutFallbackBean.class));
}
@Test
@ -985,7 +987,9 @@ class AutowiredAnnotationBeanPostProcessorTests { @@ -985,7 +987,9 @@ class AutowiredAnnotationBeanPostProcessorTests {
bf.registerBeanDefinition("testBean3", tb);
bf.getBean("testBean3");
assertThat(bf.getBean("annotatedBean", ConstructorWithoutFallbackBean.class).getTestBean3()).isNull();
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
.isThrownBy(() -> bf.getBean("annotatedBean"))
.satisfies(methodParameterDeclaredOn(ConstructorWithoutFallbackBean.class));
}
@Test
@ -2733,22 +2737,22 @@ class AutowiredAnnotationBeanPostProcessorTests { @@ -2733,22 +2737,22 @@ class AutowiredAnnotationBeanPostProcessorTests {
public static class ResourceInjectionBean {
@Autowired(required = false)
private TestBean testBean;
private @Nullable TestBean testBean;
TestBean testBean2;
@Nullable TestBean testBean2;
@Autowired
public void setTestBean2(TestBean testBean2) {
public void setTestBean2(@Nullable TestBean testBean2) {
Assert.state(this.testBean != null, "Wrong initialization order");
Assert.state(this.testBean2 == null, "Already called");
this.testBean2 = testBean2;
}
public TestBean getTestBean() {
public @Nullable TestBean getTestBean() {
return this.testBean;
}
public TestBean getTestBean2() {
public @Nullable TestBean getTestBean2() {
return this.testBean2;
}
}
@ -2757,13 +2761,13 @@ class AutowiredAnnotationBeanPostProcessorTests { @@ -2757,13 +2761,13 @@ class AutowiredAnnotationBeanPostProcessorTests {
static class NonPublicResourceInjectionBean<T> extends ResourceInjectionBean {
@Autowired
public final ITestBean testBean3 = null;
public final @Nullable ITestBean testBean3 = null;
private T nestedTestBean;
private @Nullable T nestedTestBean;
private ITestBean testBean4;
private @Nullable ITestBean testBean4;
protected BeanFactory beanFactory;
protected @Nullable BeanFactory beanFactory;
public boolean baseInjected = false;
@ -2772,18 +2776,18 @@ class AutowiredAnnotationBeanPostProcessorTests { @@ -2772,18 +2776,18 @@ class AutowiredAnnotationBeanPostProcessorTests {
@Override
@Autowired
public void setTestBean2(TestBean testBean2) {
public void setTestBean2(@Nullable TestBean testBean2) {
this.testBean2 = testBean2;
}
@Autowired
private void inject(ITestBean testBean4, T nestedTestBean) {
private void inject(@Nullable ITestBean testBean4, @Nullable T nestedTestBean) {
this.testBean4 = testBean4;
this.nestedTestBean = nestedTestBean;
}
@Autowired
private void inject(ITestBean testBean4) {
private void inject(@Nullable ITestBean testBean4) {
this.baseInjected = true;
}
@ -2793,11 +2797,11 @@ class AutowiredAnnotationBeanPostProcessorTests { @@ -2793,11 +2797,11 @@ class AutowiredAnnotationBeanPostProcessorTests {
this.beanFactory = beanFactory;
}
public ITestBean getTestBean3() {
public @Nullable ITestBean getTestBean3() {
return this.testBean3;
}
public ITestBean getTestBean4() {
public @Nullable ITestBean getTestBean4() {
return this.testBean4;
}

Loading…
Cancel
Save