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 93c909e047b6..696a9c1cd59e 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 77c3d66597fe..74caf783f3f7 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 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. @@ -665,7 +665,7 @@ private Object resolveFieldValue(Field field, Object bean, @Nullable String bean 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())) { @@ -778,7 +778,7 @@ private Object[] resolveMethodArguments(Method method, Object bean, @Nullable St 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 b2ad93854e2a..9c3cb63f4341 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 @@ -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. @@ -133,6 +133,26 @@ public void testResourceInjection() { assertThat(bean.getTestBean2()).isSameAs(tb); } + @Test + public void testResourceInjectionWithNullBean() { + 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 public void testExtendedResourceInjection() { RootBeanDefinition bd = new RootBeanDefinition(TypedExtendedResourceInjectionBean.class);