diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java index 93c909e047b..696a9c1cd59 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2023 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. @@ -136,7 +136,10 @@ public interface BeanFactory { *
Translates aliases back to the corresponding canonical bean name. *
Will ask the parent factory if the bean cannot be found in this factory instance. * @param name the name of the bean to retrieve - * @return an instance of the bean + * @return an instance of the bean. + * Note that the return value will never be {@code null} but possibly a stub for + * {@code null} returned from a factory method, to be checked via {@code equals(null)}. + * Consider using {@link #getBeanProvider(Class)} for resolving optional dependencies. * @throws NoSuchBeanDefinitionException if there is no bean with the specified name * @throws BeansException if the bean could not be obtained */ @@ -152,7 +155,11 @@ public interface BeanFactory { *
Will ask the parent factory if the bean cannot be found in this factory instance. * @param name the name of the bean to retrieve * @param requiredType type the bean must match; can be an interface or superclass - * @return an instance of the bean + * @return an instance of the bean. + * Note that the return value will never be {@code null}. In case of a stub for + * {@code null} from a factory method having been resolved for the requested bean, a + * {@code BeanNotOfRequiredTypeException} against the NullBean stub will be raised. + * Consider using {@link #getBeanProvider(Class)} for resolving optional dependencies. * @throws NoSuchBeanDefinitionException if there is no such bean definition * @throws BeanNotOfRequiredTypeException if the bean is not of the required type * @throws BeansException if the bean could not be created diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java index 6163667f9a8..3636149d612 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java @@ -719,7 +719,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA if (value != null || this.required) { cachedFieldValue = desc; registerDependentBeans(beanName, autowiredBeanNames); - if (autowiredBeanNames.size() == 1) { + if (value != null && autowiredBeanNames.size() == 1) { String autowiredBeanName = autowiredBeanNames.iterator().next(); if (beanFactory.containsBean(autowiredBeanName) && beanFactory.isTypeMatch(autowiredBeanName, field.getType())) { @@ -829,7 +829,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA Class>[] paramTypes = method.getParameterTypes(); for (int i = 0; i < paramTypes.length; i++) { String autowiredBeanName = it.next(); - if (beanFactory.containsBean(autowiredBeanName) && + if (arguments[i] != null && beanFactory.containsBean(autowiredBeanName) && beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) { cachedMethodArguments[i] = new ShortcutDependencyDescriptor( descriptors[i], autowiredBeanName, paramTypes[i]); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java index 8a57c8d68c4..781de2ef115 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java @@ -130,6 +130,26 @@ public class AutowiredAnnotationBeanPostProcessorTests { assertThat(bean.getTestBean2()).isSameAs(tb); } + @Test + void resourceInjectionWithNullBean() { + RootBeanDefinition bd = new RootBeanDefinition(NonPublicResourceInjectionBean.class); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); + bf.registerBeanDefinition("annotatedBean", bd); + RootBeanDefinition tb = new RootBeanDefinition(NullFactoryMethods.class); + tb.setFactoryMethodName("createTestBean"); + bf.registerBeanDefinition("testBean", tb); + + NonPublicResourceInjectionBean bean = (NonPublicResourceInjectionBean) bf.getBean("annotatedBean"); + assertThat(bean.getTestBean()).isNull(); + assertThat(bean.getTestBean2()).isNull(); + assertThat(bean.getTestBean3()).isNull(); + + bean = (NonPublicResourceInjectionBean) bf.getBean("annotatedBean"); + assertThat(bean.getTestBean()).isNull(); + assertThat(bean.getTestBean2()).isNull(); + assertThat(bean.getTestBean3()).isNull(); + } + @Test void extendedResourceInjection() { RootBeanDefinition bd = new RootBeanDefinition(TypedExtendedResourceInjectionBean.class);