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 10ac3533fa30..04a9db38b935 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 @@ -118,6 +118,7 @@ * @author Phillip Webb * @author Stephane Nicoll * @author Sebastien Deleuze + * @author Yanming Zhou * @since 16 April 2001 * @see #registerBeanDefinition * @see #addBeanPostProcessor @@ -1479,6 +1480,9 @@ else if (candidateNames.length > 1) { if (candidateName == null) { candidateName = determineHighestPriorityCandidate(candidates, requiredType.toClass()); } + if (candidateName == null) { + candidateName = determineEffectivelyPrimaryCandidate(candidates, requiredType.toClass()); + } if (candidateName != null) { Object beanInstance = candidates.get(candidateName); if (beanInstance == null) { @@ -2046,6 +2050,34 @@ else if (candidatePriority < highestPriority) { return highestPriorityBeanName; } + /** + * Determine the candidate which is the only one default candidate in the given set of beans. + * @param candidates a Map of candidate names and candidate instances + * (or candidate classes if not created yet) that match the required type + * @param requiredType the target dependency type to match against + * @return the name of the only one default candidate, + * or {@code null} if none found + */ + @Nullable + protected String determineEffectivelyPrimaryCandidate(Map candidates, Class requiredType) { + String candidateBeanName = null; + for (Map.Entry entry : candidates.entrySet()) { + String transformedBeanName = transformedBeanName(entry.getKey()); + if (containsBeanDefinition(transformedBeanName)) { + RootBeanDefinition bd = getMergedLocalBeanDefinition(transformedBeanName); + if (bd.isAutowireCandidate() && bd.isDefaultCandidate()) { + if (candidateBeanName == null) { + candidateBeanName = entry.getKey(); + } + else { + return null; + } + } + } + } + return candidateBeanName; + } + /** * Return whether the bean definition for the given bean name has been * marked as a primary bean. diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java index d3305a035ee8..b5dd4563b163 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java @@ -113,6 +113,7 @@ * @author Chris Beams * @author Phillip Webb * @author Stephane Nicoll + * @author Yanming Zhou */ class DefaultListableBeanFactoryTests { @@ -1664,6 +1665,18 @@ void getBeanByTypeWithPrimary() { assertThat(lbf.containsSingleton("bd1")).isFalse(); } + @Test + void getBeanByTypeWithEffectivelyPrimary() { + RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class); + bd1.setDefaultCandidate(false); + RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class); + lbf.registerBeanDefinition("bd1", bd1); + lbf.registerBeanDefinition("bd2", bd2); + + TestBean bean = lbf.getBean(TestBean.class); + assertThat(bean.getBeanName()).isEqualTo("bd2"); + } + @Test @SuppressWarnings("rawtypes") void getFactoryBeanByTypeWithPrimary() {