1. We have a bean factory contains business logic which might returns null.
  2. Our service component will needs to inject this nullable bean.

In this case, when we try to fetch service instance from spring context, we will face NoSuchBeanDefinitionException. Below is the sample UT to reproduce. (spring framework v5.3.37)

Can we consider to support null injecting in future release ? If not, do we have any recommendation for our use case ?

public class NullBeanTest {

    @Test
    void test() {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(TestService.class, BeanFactory.class);

        TestService service = ctx.getBean(TestService.class);
        assertThat(service.getNullableBean()).isNull();

        ctx.close();
    }

    @Component
    @Scope("prototype")
    static class TestService {

        @Autowired
        private NullableBean nullableBean;

        public NullableBean getNullableBean() {
            return nullableBean;
        }
    }

    static class NullableBean{

    }

    @Component
    static class BeanFactory {

        @Bean("nullableBean")
        @Scope("prototype")
        public NullableBean create(){
            return null;
        }
    }
}

Comment From: sbrannen

Thanks for getting in touch, but it feels like this is a question that would be better suited to Stack Overflow. As mentioned in the guidelines for contributing, we prefer to use the issue tracker only for bugs and enhancements. Feel free to update this issue with a link to the re-posted question (so that other people can find it) or add some more details if you feel this is a genuine bug.


Having said that, the solution to your problem is to use @Autowired(required = false) on the TestService.nullableBean field.

Comment From: felixzhang0818

Hi @sbrannen

I don't think this is a simple question. Actually, we do need support or enhancement.

In current behavior, below two cases will both results in NoSuchBeanDefinitionException.

  1. The real case, that we don't have related bean definition
  2. We do have bean definition, but the bean factory returns null.

These two cases are totally different. If we simply use @Autowired(required=false), yes, we can get null , but we don't know the real reason in application side. Reason1 or Reason2 ?

My request is, can we consider to officially support injecting null ? So that, when we get null, we will know the reason is bean factory is returning null, when we face exception, we will know the reason is no such bean defined.

Comment From: sbrannen

Hi @felixzhang0818,

My request is, can we consider to officially support injecting null ? So that, when we get null, we will know the reason is bean factory is returning null, when we face exception, we will know the reason is no such bean defined.

I think that is a rather unusual request. It's typically sufficient to use @Autowired(required = false) NullableBean, Optional<NullableBean>, or ObjectProvider<NullableBean>.

Note, however, that it is possible to support your use case if you retrieve the NullableBean directly from the ListableBeanFactory by checking whether a call to getBean() returns Spring's internal NullBean.

For example, try the following.

@Component
@Scope("prototype")
static class TestService {

    @Autowired
    private ListableBeanFactory beanFactory;

    public NullableBean getNullableBean() {
        String[] beanNames = this.beanFactory.getBeanNamesForType(NullableBean.class);
        if (beanNames.length != 1) {
            throw new RuntimeException("No single NullableBean found");
        }
        Object bean = this.beanFactory.getBean(beanNames[0]);
        // Check for org.springframework.beans.factory.support.NullBean
        if (bean != null && bean.equals(null) && "null".equals(bean.toString())) {
            System.out.println("NullableBean is null");
            return null;
        }
        return (NullableBean) bean;
    }
}

You can of course tweak the above to suit your purposes.

Regards,

Sam

Comment From: sbrannen

After discussing this with @jhoeller, it turns out the above can be simplified by first checking containsBean(). The downside is that you have to know the name of the nullableBean.

For example:

@Component
@Scope("prototype")
static class TestService {

    @Autowired
    private BeanFactory beanFactory;

    public NullableBean getNullableBean() {
        if (this.beanFactory.containsBean("nullableBean")) {
            return this.beanFactory.getBeanProvider(NullableBean.class).getIfAvailable();
        }
        throw new NoSuchBeanDefinitionException(NullableBean.class);
    }
}

Note that I changed the name of your BeanFactory class to Config to avoid the conflict with org.springframework.beans.factory.BeanFactory.