Skip to content

Commit 8f103c2

Browse files
jhoellerunknown
authored andcommitted
Introduced NoUniqueBeanDefinitionException as a dedicated subclass of NoSuchBeanDefinitionException
Issue: SPR-10194
1 parent 1a929f2 commit 8f103c2

File tree

8 files changed

+163
-59
lines changed

8 files changed

+163
-59
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -143,7 +143,7 @@ public interface BeanFactory {
143143
* is {@code Object.class}, this method will succeed whatever the class of the
144144
* returned instance.
145145
* @return an instance of the bean
146-
* @throws NoSuchBeanDefinitionException if there's no such bean definition
146+
* @throws NoSuchBeanDefinitionException if there is no such bean definition
147147
* @throws BeanNotOfRequiredTypeException if the bean is not of the required type
148148
* @throws BeansException if the bean could not be created
149149
*/
@@ -158,7 +158,8 @@ public interface BeanFactory {
158158
* of the given type. For more extensive retrieval operations across sets of beans,
159159
* use {@link ListableBeanFactory} and/or {@link BeanFactoryUtils}.
160160
* @return an instance of the single bean matching the required type
161-
* @throws NoSuchBeanDefinitionException if there is not exactly one matching bean found
161+
* @throws NoSuchBeanDefinitionException if no bean of the given type was found
162+
* @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found
162163
* @since 3.0
163164
* @see ListableBeanFactory
164165
*/
@@ -172,7 +173,7 @@ public interface BeanFactory {
172173
* @param args arguments to use if creating a prototype using explicit arguments to a
173174
* static factory method. It is invalid to use a non-null args value in any other case.
174175
* @return an instance of the bean
175-
* @throws NoSuchBeanDefinitionException if there's no such bean definition
176+
* @throws NoSuchBeanDefinitionException if there is no such bean definition
176177
* @throws BeanDefinitionStoreException if arguments have been given but
177178
* the affected bean isn't a prototype
178179
* @throws BeansException if the bean could not be created

spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryUtils.java

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -310,20 +310,15 @@ public static <T> Map<String, T> beansOfTypeIncludingAncestors(
310310
* @param lbf the bean factory
311311
* @param type type of bean to match
312312
* @return the matching bean instance
313-
* @throws NoSuchBeanDefinitionException
314-
* if 0 or more than 1 beans of the given type were found
313+
* @throws NoSuchBeanDefinitionException if no bean of the given type was found
314+
* @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found
315315
* @throws BeansException if the bean could not be created
316316
*/
317317
public static <T> T beanOfTypeIncludingAncestors(ListableBeanFactory lbf, Class<T> type)
318318
throws BeansException {
319319

320320
Map<String, T> beansOfType = beansOfTypeIncludingAncestors(lbf, type);
321-
if (beansOfType.size() == 1) {
322-
return beansOfType.values().iterator().next();
323-
}
324-
else {
325-
throw new NoSuchBeanDefinitionException(type, "expected single bean but found " + beansOfType.size());
326-
}
321+
return uniqueBean(type, beansOfType);
327322
}
328323

329324
/**
@@ -351,21 +346,16 @@ public static <T> T beanOfTypeIncludingAncestors(ListableBeanFactory lbf, Class<
351346
* eagerly initialized to determine their type: So be aware that passing in "true"
352347
* for this flag will initialize FactoryBeans and "factory-bean" references.
353348
* @return the matching bean instance
354-
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
355-
* if 0 or more than 1 beans of the given type were found
349+
* @throws NoSuchBeanDefinitionException if no bean of the given type was found
350+
* @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found
356351
* @throws BeansException if the bean could not be created
357352
*/
358353
public static <T> T beanOfTypeIncludingAncestors(
359354
ListableBeanFactory lbf, Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
360355
throws BeansException {
361356

362357
Map<String, T> beansOfType = beansOfTypeIncludingAncestors(lbf, type, includeNonSingletons, allowEagerInit);
363-
if (beansOfType.size() == 1) {
364-
return beansOfType.values().iterator().next();
365-
}
366-
else {
367-
throw new NoSuchBeanDefinitionException(type, "expected single bean but found " + beansOfType.size());
368-
}
358+
return uniqueBean(type, beansOfType);
369359
}
370360

371361
/**
@@ -380,19 +370,14 @@ public static <T> T beanOfTypeIncludingAncestors(
380370
* @param lbf the bean factory
381371
* @param type type of bean to match
382372
* @return the matching bean instance
383-
* @throws NoSuchBeanDefinitionException
384-
* if 0 or more than 1 beans of the given type were found
373+
* @throws NoSuchBeanDefinitionException if no bean of the given type was found
374+
* @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found
385375
* @throws BeansException if the bean could not be created
386376
*/
387377
public static <T> T beanOfType(ListableBeanFactory lbf, Class<T> type) throws BeansException {
388378
Assert.notNull(lbf, "ListableBeanFactory must not be null");
389379
Map<String, T> beansOfType = lbf.getBeansOfType(type);
390-
if (beansOfType.size() == 1) {
391-
return beansOfType.values().iterator().next();
392-
}
393-
else {
394-
throw new NoSuchBeanDefinitionException(type, "expected single bean but found " + beansOfType.size());
395-
}
380+
return uniqueBean(type, beansOfType);
396381
}
397382

398383
/**
@@ -415,8 +400,8 @@ public static <T> T beanOfType(ListableBeanFactory lbf, Class<T> type) throws Be
415400
* eagerly initialized to determine their type: So be aware that passing in "true"
416401
* for this flag will initialize FactoryBeans and "factory-bean" references.
417402
* @return the matching bean instance
418-
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
419-
* if 0 or more than 1 beans of the given type were found
403+
* @throws NoSuchBeanDefinitionException if no bean of the given type was found
404+
* @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found
420405
* @throws BeansException if the bean could not be created
421406
*/
422407
public static <T> T beanOfType(
@@ -425,11 +410,27 @@ public static <T> T beanOfType(
425410

426411
Assert.notNull(lbf, "ListableBeanFactory must not be null");
427412
Map<String, T> beansOfType = lbf.getBeansOfType(type, includeNonSingletons, allowEagerInit);
428-
if (beansOfType.size() == 1) {
429-
return beansOfType.values().iterator().next();
413+
return uniqueBean(type, beansOfType);
414+
}
415+
416+
/**
417+
* Extract a unique bean for the given type from the given Map of matching beans.
418+
* @param type type of bean to match
419+
* @param matchingBeans all matching beans found
420+
* @return the unique bean instance
421+
* @throws NoSuchBeanDefinitionException if no bean of the given type was found
422+
* @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found
423+
*/
424+
private static <T> T uniqueBean(Class<T> type, Map<String, T> matchingBeans) {
425+
int nrFound = matchingBeans.size();
426+
if (nrFound == 1) {
427+
return matchingBeans.values().iterator().next();
428+
}
429+
else if (nrFound > 1) {
430+
throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet());
430431
}
431432
else {
432-
throw new NoSuchBeanDefinitionException(type, "expected single bean but found " + beansOfType.size());
433+
throw new NoSuchBeanDefinitionException(type);
433434
}
434435
}
435436

spring-beans/src/main/java/org/springframework/beans/factory/NoSuchBeanDefinitionException.java

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,11 +20,13 @@
2020
import org.springframework.util.StringUtils;
2121

2222
/**
23-
* Exception thrown when a {@code BeanFactory} is asked for a bean
24-
* instance for which it cannot find a definition.
23+
* Exception thrown when a {@code BeanFactory} is asked for a bean instance
24+
* for which it cannot find a definition.
2525
*
2626
* @author Rod Johnson
2727
* @author Juergen Hoeller
28+
* @see BeanFactory#getBean(String)
29+
* @see BeanFactory#getBean(Class)
2830
*/
2931
@SuppressWarnings("serial")
3032
public class NoSuchBeanDefinitionException extends BeansException {
@@ -60,7 +62,7 @@ public NoSuchBeanDefinitionException(String name, String message) {
6062
* @param type required type of the missing bean
6163
*/
6264
public NoSuchBeanDefinitionException(Class<?> type) {
63-
super("No unique bean of type [" + type.getName() + "] is defined");
65+
super("No qualifying bean of type [" + type.getName() + "] is defined");
6466
this.beanType = type;
6567
}
6668

@@ -70,7 +72,7 @@ public NoSuchBeanDefinitionException(Class<?> type) {
7072
* @param message detailed message describing the problem
7173
*/
7274
public NoSuchBeanDefinitionException(Class<?> type, String message) {
73-
super("No unique bean of type [" + type.getName() + "] is defined: " + message);
75+
super("No qualifying bean of type [" + type.getName() + "] is defined: " + message);
7476
this.beanType = type;
7577
}
7678

@@ -81,27 +83,34 @@ public NoSuchBeanDefinitionException(Class<?> type, String message) {
8183
* @param message detailed message describing the problem
8284
*/
8385
public NoSuchBeanDefinitionException(Class<?> type, String dependencyDescription, String message) {
84-
super("No matching bean of type [" + type.getName() + "] found for dependency" +
86+
super("No qualifying bean of type [" + type.getName() + "] found for dependency" +
8587
(StringUtils.hasLength(dependencyDescription) ? " [" + dependencyDescription + "]" : "") +
8688
": " + message);
8789
this.beanType = type;
8890
}
8991

9092

9193
/**
92-
* Return the name of the missing bean, if it was a lookup <em>by name</em>
93-
* that failed.
94+
* Return the name of the missing bean, if it was a lookup <em>by name</em> that failed.
9495
*/
9596
public String getBeanName() {
9697
return this.beanName;
9798
}
9899

99100
/**
100-
* Return the required type of the missing bean, if it was a lookup
101-
* <em>by type</em> that failed.
101+
* Return the required type of the missing bean, if it was a lookup <em>by type</em> that failed.
102102
*/
103103
public Class<?> getBeanType() {
104104
return this.beanType;
105105
}
106106

107+
/**
108+
* Return the number of beans found when only one matching bean was expected.
109+
* For a regular NoSuchBeanDefinitionException, this will always be 0.
110+
* @see NoUniqueBeanDefinitionException
111+
*/
112+
public int getNumberOfBeansFound() {
113+
return 0;
114+
}
115+
107116
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.beans.factory;
18+
19+
import java.util.Arrays;
20+
import java.util.Collection;
21+
22+
import org.springframework.util.StringUtils;
23+
24+
/**
25+
* Exception thrown when a {@code BeanFactory} is asked for a bean instance for which
26+
* multiple matching candidates have been found when only one matching bean was expected.
27+
*
28+
* @author Juergen Hoeller
29+
* @since 3.2.1
30+
* @see BeanFactory#getBean(Class)
31+
*/
32+
@SuppressWarnings("serial")
33+
public class NoUniqueBeanDefinitionException extends NoSuchBeanDefinitionException {
34+
35+
private int numberOfBeansFound;
36+
37+
38+
/**
39+
* Create a new {@code NoUniqueBeanDefinitionException}.
40+
* @param type required type of the non-unique bean
41+
* @param numberOfBeansFound the number of matching beans
42+
* @param message detailed message describing the problem
43+
*/
44+
public NoUniqueBeanDefinitionException(Class<?> type, int numberOfBeansFound, String message) {
45+
super(type, message);
46+
this.numberOfBeansFound = numberOfBeansFound;
47+
}
48+
49+
/**
50+
* Create a new {@code NoUniqueBeanDefinitionException}.
51+
* @param type required type of the non-unique bean
52+
* @param beanNamesFound the names of all matching beans (as a Collection)
53+
*/
54+
public NoUniqueBeanDefinitionException(Class<?> type, Collection<String> beanNamesFound) {
55+
this(type, beanNamesFound.size(), "expected single matching bean but found " + beanNamesFound.size() + ": " +
56+
StringUtils.collectionToCommaDelimitedString(beanNamesFound));
57+
}
58+
59+
/**
60+
* Create a new {@code NoUniqueBeanDefinitionException}.
61+
* @param type required type of the non-unique bean
62+
* @param beanNamesFound the names of all matching beans (as an array)
63+
*/
64+
public NoUniqueBeanDefinitionException(Class<?> type, String... beanNamesFound) {
65+
this(type, Arrays.asList(beanNamesFound));
66+
}
67+
68+
69+
/**
70+
* Return the number of beans found when only one matching bean was expected.
71+
* For a NoUniqueBeanDefinitionException, this will usually be higher than 1.
72+
* @see #getBeanType()
73+
*/
74+
@Override
75+
public int getNumberOfBeansFound() {
76+
return this.numberOfBeansFound;
77+
}
78+
79+
}

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -50,6 +50,7 @@
5050
import org.springframework.beans.factory.CannotLoadBeanClassException;
5151
import org.springframework.beans.factory.FactoryBean;
5252
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
53+
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
5354
import org.springframework.beans.factory.ObjectFactory;
5455
import org.springframework.beans.factory.SmartFactoryBean;
5556
import org.springframework.beans.factory.config.BeanDefinition;
@@ -270,12 +271,14 @@ public <T> T getBean(Class<T> requiredType) throws BeansException {
270271
if (beanNames.length == 1) {
271272
return getBean(beanNames[0], requiredType);
272273
}
273-
else if (beanNames.length == 0 && getParentBeanFactory() != null) {
274+
else if (beanNames.length > 1) {
275+
throw new NoUniqueBeanDefinitionException(requiredType, beanNames);
276+
}
277+
else if (getParentBeanFactory() != null) {
274278
return getParentBeanFactory().getBean(requiredType);
275279
}
276280
else {
277-
throw new NoSuchBeanDefinitionException(requiredType, "expected single bean but found " +
278-
beanNames.length + ": " + StringUtils.arrayToCommaDelimitedString(beanNames));
281+
throw new NoSuchBeanDefinitionException(requiredType);
279282
}
280283
}
281284

@@ -823,8 +826,7 @@ else if (Map.class.isAssignableFrom(type) && type.isInterface()) {
823826
if (matchingBeans.size() > 1) {
824827
String primaryBeanName = determinePrimaryCandidate(matchingBeans, descriptor);
825828
if (primaryBeanName == null) {
826-
throw new NoSuchBeanDefinitionException(type, "expected single matching bean but found " +
827-
matchingBeans.size() + ": " + matchingBeans.keySet());
829+
throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet());
828830
}
829831
if (autowiredBeanNames != null) {
830832
autowiredBeanNames.add(primaryBeanName);
@@ -895,7 +897,7 @@ protected String determinePrimaryCandidate(Map<String, Object> candidateBeans, D
895897
boolean candidateLocal = containsBeanDefinition(candidateBeanName);
896898
boolean primaryLocal = containsBeanDefinition(primaryBeanName);
897899
if (candidateLocal == primaryLocal) {
898-
throw new NoSuchBeanDefinitionException(descriptor.getDependencyType(),
900+
throw new NoUniqueBeanDefinitionException(descriptor.getDependencyType(), candidateBeans.size(),
899901
"more than one 'primary' bean found among candidates: " + candidateBeans.keySet());
900902
}
901903
else if (candidateLocal && !primaryLocal) {

spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2009 the original author or authors.
2+
* Copyright 2002-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@
3131
import org.springframework.beans.factory.FactoryBean;
3232
import org.springframework.beans.factory.ListableBeanFactory;
3333
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
34+
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
3435
import org.springframework.beans.factory.SmartFactoryBean;
3536
import org.springframework.core.annotation.AnnotationUtils;
3637
import org.springframework.util.StringUtils;
@@ -117,8 +118,11 @@ public <T> T getBean(Class<T> requiredType) throws BeansException {
117118
if (beanNames.length == 1) {
118119
return getBean(beanNames[0], requiredType);
119120
}
121+
else if (beanNames.length > 1) {
122+
throw new NoUniqueBeanDefinitionException(requiredType, beanNames);
123+
}
120124
else {
121-
throw new NoSuchBeanDefinitionException(requiredType, "expected single bean but found " + beanNames.length);
125+
throw new NoSuchBeanDefinitionException(requiredType);
122126
}
123127
}
124128

0 commit comments

Comments
 (0)