Enforce non-null value from getBean and at injection points

Bean-derived null values may still get passed into bean properties and injection points but only if those are declared as non-required. Note that getBean will never return null; a manual bean.equals(null) / "null".equals(bean.toString()) check identifies expected null values now.  This will only ever happen with custom FactoryBeans or factory methods returning null - and since all common cases are handled by autowiring or bean property values in bean definitions, there should be no need to ever manually check for such a null value received from getBean.

Issue: SPR-15829
This commit is contained in:
Juergen Hoeller
2017-08-18 00:11:35 +02:00
parent 10dcaa9bf6
commit b94302b5bd
30 changed files with 344 additions and 204 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -51,8 +51,8 @@ public class FactoryBeanTests {
public void testFactoryBeanReturnsNull() throws Exception {
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(factory).loadBeanDefinitions(RETURNS_NULL_CONTEXT);
Object result = factory.getBean("factoryBean");
assertNull(result);
assertEquals("null", factory.getBean("factoryBean").toString());
}
@Test

View File

@@ -572,8 +572,9 @@ public class AutowiredAnnotationBeanPostProcessorTests {
}
@Test
public void testConstructorResourceInjectionWithNull() {
public void testConstructorResourceInjectionWithNullFromFactoryBean() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
bf.registerResolvableDependency(BeanFactory.class, bf);
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
@@ -603,6 +604,42 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertSame(bf, bean.getBeanFactory());
}
@Test
public void testConstructorResourceInjectionWithNullFromFactoryMethod() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
bf.registerResolvableDependency(BeanFactory.class, bf);
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
bf.addBeanPostProcessor(bpp);
RootBeanDefinition bd = new RootBeanDefinition(ConstructorResourceInjectionBean.class);
bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
bf.registerBeanDefinition("annotatedBean", bd);
RootBeanDefinition tb = new RootBeanDefinition(NullFactoryMethods.class);
tb.setFactoryMethodName("createTestBean");
bf.registerBeanDefinition("testBean", tb);
RootBeanDefinition ntb = new RootBeanDefinition(NullFactoryMethods.class);
ntb.setFactoryMethodName("createNestedTestBean");
bf.registerBeanDefinition("nestedTestBean", ntb);
bf.registerSingleton("nestedTestBean2", new NestedTestBean());
ConstructorResourceInjectionBean bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
assertNull(bean.getTestBean());
assertNull(bean.getTestBean2());
assertNull(bean.getTestBean3());
assertNull(bean.getTestBean4());
assertNull(bean.getNestedTestBean());
assertSame(bf, bean.getBeanFactory());
bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
assertNull(bean.getTestBean());
assertNull(bean.getTestBean2());
assertNull(bean.getTestBean3());
assertNull(bean.getTestBean4());
assertNull(bean.getNestedTestBean());
assertSame(bf, bean.getBeanFactory());
}
@Test
public void testConstructorResourceInjectionWithMultipleCandidates() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
@@ -756,6 +793,33 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.destroySingletons();
}
@Test(expected = UnsatisfiedDependencyException.class)
public void testSingleConstructorInjectionWithMissingDependency() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
bf.addBeanPostProcessor(bpp);
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SingleConstructorCollectionInjectionBean.class));
bf.getBean("annotatedBean");
}
@Test(expected = UnsatisfiedDependencyException.class)
public void testSingleConstructorInjectionWithNullDependency() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
bf.addBeanPostProcessor(bpp);
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SingleConstructorCollectionInjectionBean.class));
RootBeanDefinition tb = new RootBeanDefinition(NullFactoryMethods.class);
tb.setFactoryMethodName("createTestBean");
bf.registerBeanDefinition("testBean", tb);
bf.getBean("annotatedBean");
}
@Test
public void testConstructorResourceInjectionWithMultipleCandidatesAndFallback() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
@@ -2634,7 +2698,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
public static class ConstructorResourceInjectionBean extends ResourceInjectionBean {
@Autowired
@Autowired(required = false)
protected ITestBean testBean3;
private ITestBean testBean4;
@@ -2643,7 +2707,6 @@ public class AutowiredAnnotationBeanPostProcessorTests {
private ConfigurableListableBeanFactory beanFactory;
public ConstructorResourceInjectionBean() {
throw new UnsupportedOperationException();
}
@@ -2653,7 +2716,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
}
@Autowired
public ConstructorResourceInjectionBean(ITestBean testBean4, NestedTestBean nestedTestBean,
public ConstructorResourceInjectionBean(@Autowired(required = false) ITestBean testBean4,
@Autowired(required = false) NestedTestBean nestedTestBean,
ConfigurableListableBeanFactory beanFactory) {
this.testBean4 = testBean4;
this.nestedTestBean = nestedTestBean;
@@ -2669,7 +2733,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
}
@Override
@Autowired
@Autowired(required = false)
public void setTestBean2(TestBean testBean2) {
super.setTestBean2(testBean2);
}
@@ -3802,6 +3866,18 @@ public class AutowiredAnnotationBeanPostProcessorTests {
}
public static class NullFactoryMethods {
public static TestBean createTestBean() {
return null;
}
public static NestedTestBean createNestedTestBean() {
return null;
}
}
public static class ProvidedArgumentBean {
public ProvidedArgumentBean(String[] args) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,6 +38,7 @@ public class PropertyPathFactoryBeanTests {
private static final Resource CONTEXT = qualifiedResource(PropertyPathFactoryBeanTests.class, "context.xml");
@Test
public void testPropertyPathFactoryBeanWithSingletonResult() {
DefaultListableBeanFactory xbf = new DefaultListableBeanFactory();
@@ -78,7 +79,7 @@ public class PropertyPathFactoryBeanTests {
DefaultListableBeanFactory xbf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(xbf).loadBeanDefinitions(CONTEXT);
assertNull(xbf.getType("tb.spouse.spouse"));
assertNull(xbf.getBean("tb.spouse.spouse"));
assertEquals("null", xbf.getBean("tb.spouse.spouse").toString());
}
@Test
@@ -92,4 +93,18 @@ public class PropertyPathFactoryBeanTests {
assertSame(spouse, tbWithInner.getFriends().iterator().next());
}
@Test
public void testPropertyPathFactoryBeanAsNullReference() {
DefaultListableBeanFactory xbf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(xbf).loadBeanDefinitions(CONTEXT);
assertNull(xbf.getBean("tbWithNullReference", TestBean.class).getSpouse());
}
@Test
public void testPropertyPathFactoryBeanAsInnerNull() {
DefaultListableBeanFactory xbf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(xbf).loadBeanDefinitions(CONTEXT);
assertNull(xbf.getBean("tbWithInnerNull", TestBean.class).getSpouse());
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -91,8 +91,7 @@ public class FactoryMethodTests {
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(xbf);
reader.loadBeanDefinitions(new ClassPathResource("factory-methods.xml", getClass()));
FactoryMethods fm = (FactoryMethods) xbf.getBean("null");
assertNull(fm);
assertEquals("null", xbf.getBean("null").toString());
try {
xbf.getBean("nullWithProperty");