From 00b56c026ac2ce955c6107099f9cf2daf63d37e0 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 16 Dec 2020 22:27:41 +0100 Subject: [PATCH] Consistent handling of NullBean instances in resolveNamedBean Closes gh-26271 --- .../factory/support/AbstractBeanFactory.java | 19 ++++++++++------- .../support/DefaultListableBeanFactory.java | 21 +++++++++++++++---- ...notationConfigApplicationContextTests.java | 6 ++++++ 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java index 443b7ee586d..562c00fd55f 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java @@ -250,7 +250,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp throws BeansException { String beanName = transformedBeanName(name); - Object bean; + Object beanInstance; // Eagerly check singleton cache for manually registered singletons. Object sharedInstance = getSingleton(beanName); @@ -264,7 +264,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp logger.trace("Returning cached instance of singleton bean '" + beanName + "'"); } } - bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); + beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { @@ -342,7 +342,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp throw ex; } }); - bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); + beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } else if (mbd.isPrototype()) { @@ -355,7 +355,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp finally { afterPrototypeCreation(beanName); } - bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); + beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } else { @@ -377,7 +377,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp afterPrototypeCreation(beanName); } }); - bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); + beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { throw new ScopeNotActiveException(beanName, scopeName, ex); @@ -395,14 +395,19 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp } } + return adaptBeanInstance(name, beanInstance, requiredType); + } + + @SuppressWarnings("unchecked") + T adaptBeanInstance(String name, Object bean, @Nullable Class requiredType) { // Check if required type matches the type of the actual bean instance. if (requiredType != null && !requiredType.isInstance(bean)) { try { - T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType); + Object convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType); if (convertedBean == null) { throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } - return convertedBean; + return (T) convertedBean; } catch (TypeMismatchException ex) { if (logger.isTraceEnabled()) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index dc79d3c6ad7..048612fbed7 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -1231,8 +1231,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto } if (candidateNames.length == 1) { - String beanName = candidateNames[0]; - return new NamedBeanHolder<>(beanName, (T) getBean(beanName, requiredType.toClass(), args)); + return resolveNamedBean(candidateNames[0], requiredType, args); } else if (candidateNames.length > 1) { Map candidates = CollectionUtils.newLinkedHashMap(candidateNames.length); @@ -1251,8 +1250,11 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto } if (candidateName != null) { Object beanInstance = candidates.get(candidateName); - if (beanInstance == null || beanInstance instanceof Class) { - beanInstance = getBean(candidateName, requiredType.toClass(), args); + if (beanInstance == null) { + return null; + } + if (beanInstance instanceof Class) { + return resolveNamedBean(candidateName, requiredType, args); } return new NamedBeanHolder<>(candidateName, (T) beanInstance); } @@ -1264,6 +1266,17 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto return null; } + @Nullable + private NamedBeanHolder resolveNamedBean( + String beanName, ResolvableType requiredType, @Nullable Object[] args) throws BeansException { + + Object bean = getBean(beanName, null, args); + if (bean instanceof NullBean) { + return null; + } + return new NamedBeanHolder(beanName, adaptBeanInstance(beanName, bean, requiredType.toClass())); + } + @Override @Nullable public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, diff --git a/spring-context/src/test/java/org/springframework/context/annotation/AnnotationConfigApplicationContextTests.java b/spring-context/src/test/java/org/springframework/context/annotation/AnnotationConfigApplicationContextTests.java index f2a7c86b409..336432ff0c7 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/AnnotationConfigApplicationContextTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/AnnotationConfigApplicationContextTests.java @@ -271,9 +271,15 @@ class AnnotationConfigApplicationContextTests { assertThat(ObjectUtils.containsElement(context.getBeanNamesForType(BeanA.class), "a")).isTrue(); assertThat(ObjectUtils.containsElement(context.getBeanNamesForType(BeanB.class), "b")).isTrue(); assertThat(ObjectUtils.containsElement(context.getBeanNamesForType(BeanC.class), "c")).isTrue(); + assertThat(context.getBeansOfType(BeanA.class)).isEmpty(); assertThat(context.getBeansOfType(BeanB.class).values().iterator().next()).isSameAs(context.getBean(BeanB.class)); assertThat(context.getBeansOfType(BeanC.class).values().iterator().next()).isSameAs(context.getBean(BeanC.class)); + + assertThatExceptionOfType(NoSuchBeanDefinitionException.class).isThrownBy(() -> + context.getBeanFactory().resolveNamedBean(BeanA.class)); + assertThat(context.getBeanFactory().resolveNamedBean(BeanB.class).getBeanInstance()).isSameAs(context.getBean(BeanB.class)); + assertThat(context.getBeanFactory().resolveNamedBean(BeanC.class).getBeanInstance()).isSameAs(context.getBean(BeanC.class)); } @Test