Skip to content

Commit d72c8b3

Browse files
committed
Ignore duplicate @⁠Priority values when determining highest priority
Prior to this commit, DefaultListableBeanFactory's determineHighestPriorityCandidate() method sometimes failed to determine the highest priority candidate if duplicate priority candidates were detected whose priority was not the highest priority in the candidate set. In addition, the bean registration order affected the outcome of the algorithm: if the highest priority was detected before other duplicate priorities were detected, the algorithm succeeded in determining the highest priority candidate. This commit addresses those shortcomings by ignoring duplicate @⁠Priority values unless the duplication is for the highest priority encountered, in which case a NoUniqueBeanDefinitionException is still thrown to signal that multiple beans were found with the same "highest priority". Closes gh-33733
1 parent 67d78eb commit d72c8b3

File tree

2 files changed

+91
-7
lines changed

2 files changed

+91
-7
lines changed

Diff for: spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java

+12-3
Original file line numberDiff line numberDiff line change
@@ -1776,12 +1776,15 @@ else if (candidateLocal) {
17761776
* @param requiredType the target dependency type to match against
17771777
* @return the name of the candidate with the highest priority,
17781778
* or {@code null} if none found
1779+
* @throws NoUniqueBeanDefinitionException if multiple beans are detected with
1780+
* the same highest priority value
17791781
* @see #getPriority(Object)
17801782
*/
17811783
@Nullable
17821784
protected String determineHighestPriorityCandidate(Map<String, Object> candidates, Class<?> requiredType) {
17831785
String highestPriorityBeanName = null;
17841786
Integer highestPriority = null;
1787+
boolean highestPriorityConflictDetected = false;
17851788
for (Map.Entry<String, Object> entry : candidates.entrySet()) {
17861789
String candidateBeanName = entry.getKey();
17871790
Object beanInstance = entry.getValue();
@@ -1790,13 +1793,12 @@ protected String determineHighestPriorityCandidate(Map<String, Object> candidate
17901793
if (candidatePriority != null) {
17911794
if (highestPriority != null) {
17921795
if (candidatePriority.equals(highestPriority)) {
1793-
throw new NoUniqueBeanDefinitionException(requiredType, candidates.size(),
1794-
"Multiple beans found with the same priority ('" + highestPriority +
1795-
"') among candidates: " + candidates.keySet());
1796+
highestPriorityConflictDetected = true;
17961797
}
17971798
else if (candidatePriority < highestPriority) {
17981799
highestPriorityBeanName = candidateBeanName;
17991800
highestPriority = candidatePriority;
1801+
highestPriorityConflictDetected = false;
18001802
}
18011803
}
18021804
else {
@@ -1806,6 +1808,13 @@ else if (candidatePriority < highestPriority) {
18061808
}
18071809
}
18081810
}
1811+
1812+
if (highestPriorityConflictDetected) {
1813+
throw new NoUniqueBeanDefinitionException(requiredType, candidates.size(),
1814+
"Multiple beans found with the same highest priority (" + highestPriority +
1815+
") among candidates: " + candidates.keySet());
1816+
1817+
}
18091818
return highestPriorityBeanName;
18101819
}
18111820

Diff for: spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java

+79-4
Original file line numberDiff line numberDiff line change
@@ -1729,18 +1729,73 @@ void mapInjectionWithPriority() {
17291729
assertThat(bean.getBeanName()).isEqualTo("bd1");
17301730
}
17311731

1732+
/**
1733+
* {@code determineHighestPriorityCandidate()} should reject duplicate
1734+
* priorities for the highest priority detected.
1735+
*
1736+
* @see #getBeanByTypeWithMultipleNonHighestPriorityCandidates()
1737+
*/
17321738
@Test
1733-
void getBeanByTypeWithMultiplePriority() {
1739+
void getBeanByTypeWithMultipleHighestPriorityCandidates() {
17341740
lbf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
17351741
RootBeanDefinition bd1 = new RootBeanDefinition(HighPriorityTestBean.class);
1736-
RootBeanDefinition bd2 = new RootBeanDefinition(HighPriorityTestBean.class);
1742+
RootBeanDefinition bd2 = new RootBeanDefinition(LowPriorityTestBean.class);
1743+
RootBeanDefinition bd3 = new RootBeanDefinition(HighPriorityTestBean.class);
17371744
lbf.registerBeanDefinition("bd1", bd1);
17381745
lbf.registerBeanDefinition("bd2", bd2);
1746+
lbf.registerBeanDefinition("bd3", bd3);
17391747

17401748
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class)
17411749
.isThrownBy(() -> lbf.getBean(TestBean.class))
1742-
.withMessageContaining("Multiple beans found with the same priority")
1743-
.withMessageContaining("5"); // conflicting priority
1750+
.withMessageContaining("Multiple beans found with the same highest priority (5) among candidates: ");
1751+
}
1752+
1753+
/**
1754+
* {@code determineHighestPriorityCandidate()} should ignore duplicate
1755+
* priorities for any priority other than the highest, and the order in
1756+
* which beans is declared should not affect the outcome.
1757+
*
1758+
* @see #getBeanByTypeWithMultipleHighestPriorityCandidates()
1759+
*/
1760+
@Test // gh-33733
1761+
void getBeanByTypeWithMultipleNonHighestPriorityCandidates() {
1762+
getBeanByTypeWithMultipleNonHighestPriorityCandidates(
1763+
PriorityService1.class,
1764+
PriorityService2A.class,
1765+
PriorityService2B.class,
1766+
PriorityService3.class
1767+
);
1768+
1769+
getBeanByTypeWithMultipleNonHighestPriorityCandidates(
1770+
PriorityService3.class,
1771+
PriorityService2B.class,
1772+
PriorityService2A.class,
1773+
PriorityService1.class
1774+
);
1775+
1776+
getBeanByTypeWithMultipleNonHighestPriorityCandidates(
1777+
PriorityService2A.class,
1778+
PriorityService1.class,
1779+
PriorityService2B.class,
1780+
PriorityService3.class
1781+
);
1782+
1783+
getBeanByTypeWithMultipleNonHighestPriorityCandidates(
1784+
PriorityService2A.class,
1785+
PriorityService3.class,
1786+
PriorityService1.class,
1787+
PriorityService2B.class
1788+
);
1789+
}
1790+
1791+
private void getBeanByTypeWithMultipleNonHighestPriorityCandidates(Class<?>... classes) {
1792+
lbf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
1793+
for (Class<?> clazz : classes) {
1794+
lbf.registerBeanDefinition(clazz.getSimpleName(), new RootBeanDefinition(clazz));
1795+
}
1796+
1797+
PriorityService bean = lbf.getBean(PriorityService.class);
1798+
assertThat(bean).isExactlyInstanceOf(PriorityService1.class);
17441799
}
17451800

17461801
@Test
@@ -3500,6 +3555,26 @@ public KnowsIfInstantiated() {
35003555
}
35013556

35023557

3558+
interface PriorityService {
3559+
}
3560+
3561+
@Priority(1)
3562+
static class PriorityService1 implements PriorityService {
3563+
}
3564+
3565+
@Priority(2)
3566+
static class PriorityService2A implements PriorityService {
3567+
}
3568+
3569+
@Priority(2)
3570+
static class PriorityService2B implements PriorityService {
3571+
}
3572+
3573+
@Priority(3)
3574+
static class PriorityService3 implements PriorityService {
3575+
}
3576+
3577+
35033578
@Priority(5)
35043579
private static class HighPriorityTestBean extends TestBean {
35053580
}

0 commit comments

Comments
 (0)