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 2d18e67b1a..8c73a16fd7 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-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -143,7 +143,7 @@ public interface BeanFactory { * is {@code Object.class}, this method will succeed whatever the class of the * returned instance. * @return an instance of the bean - * @throws NoSuchBeanDefinitionException if there's no such bean definition + * @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 */ @@ -158,7 +158,8 @@ public interface BeanFactory { * of the given type. For more extensive retrieval operations across sets of beans, * use {@link ListableBeanFactory} and/or {@link BeanFactoryUtils}. * @return an instance of the single bean matching the required type - * @throws NoSuchBeanDefinitionException if there is not exactly one matching bean found + * @throws NoSuchBeanDefinitionException if no bean of the given type was found + * @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found * @since 3.0 * @see ListableBeanFactory */ @@ -172,7 +173,7 @@ public interface BeanFactory { * @param args arguments to use if creating a prototype using explicit arguments to a * static factory method. It is invalid to use a non-null args value in any other case. * @return an instance of the bean - * @throws NoSuchBeanDefinitionException if there's no such bean definition + * @throws NoSuchBeanDefinitionException if there is no such bean definition * @throws BeanDefinitionStoreException if arguments have been given but * the affected bean isn't a prototype * @throws BeansException if the bean could not be created diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryUtils.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryUtils.java index 2a4f577a1c..657bb1ebd1 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryUtils.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -310,20 +310,15 @@ public abstract class BeanFactoryUtils { * @param lbf the bean factory * @param type type of bean to match * @return the matching bean instance - * @throws NoSuchBeanDefinitionException - * if 0 or more than 1 beans of the given type were found + * @throws NoSuchBeanDefinitionException if no bean of the given type was found + * @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found * @throws BeansException if the bean could not be created */ public static T beanOfTypeIncludingAncestors(ListableBeanFactory lbf, Class type) throws BeansException { Map beansOfType = beansOfTypeIncludingAncestors(lbf, type); - if (beansOfType.size() == 1) { - return beansOfType.values().iterator().next(); - } - else { - throw new NoSuchBeanDefinitionException(type, "expected single bean but found " + beansOfType.size()); - } + return uniqueBean(type, beansOfType); } /** @@ -351,8 +346,8 @@ public abstract class BeanFactoryUtils { * eagerly initialized to determine their type: So be aware that passing in "true" * for this flag will initialize FactoryBeans and "factory-bean" references. * @return the matching bean instance - * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException - * if 0 or more than 1 beans of the given type were found + * @throws NoSuchBeanDefinitionException if no bean of the given type was found + * @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found * @throws BeansException if the bean could not be created */ public static T beanOfTypeIncludingAncestors( @@ -360,12 +355,7 @@ public abstract class BeanFactoryUtils { throws BeansException { Map beansOfType = beansOfTypeIncludingAncestors(lbf, type, includeNonSingletons, allowEagerInit); - if (beansOfType.size() == 1) { - return beansOfType.values().iterator().next(); - } - else { - throw new NoSuchBeanDefinitionException(type, "expected single bean but found " + beansOfType.size()); - } + return uniqueBean(type, beansOfType); } /** @@ -380,19 +370,14 @@ public abstract class BeanFactoryUtils { * @param lbf the bean factory * @param type type of bean to match * @return the matching bean instance - * @throws NoSuchBeanDefinitionException - * if 0 or more than 1 beans of the given type were found + * @throws NoSuchBeanDefinitionException if no bean of the given type was found + * @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found * @throws BeansException if the bean could not be created */ public static T beanOfType(ListableBeanFactory lbf, Class type) throws BeansException { Assert.notNull(lbf, "ListableBeanFactory must not be null"); Map beansOfType = lbf.getBeansOfType(type); - if (beansOfType.size() == 1) { - return beansOfType.values().iterator().next(); - } - else { - throw new NoSuchBeanDefinitionException(type, "expected single bean but found " + beansOfType.size()); - } + return uniqueBean(type, beansOfType); } /** @@ -415,8 +400,8 @@ public abstract class BeanFactoryUtils { * eagerly initialized to determine their type: So be aware that passing in "true" * for this flag will initialize FactoryBeans and "factory-bean" references. * @return the matching bean instance - * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException - * if 0 or more than 1 beans of the given type were found + * @throws NoSuchBeanDefinitionException if no bean of the given type was found + * @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found * @throws BeansException if the bean could not be created */ public static T beanOfType( @@ -425,11 +410,27 @@ public abstract class BeanFactoryUtils { Assert.notNull(lbf, "ListableBeanFactory must not be null"); Map beansOfType = lbf.getBeansOfType(type, includeNonSingletons, allowEagerInit); - if (beansOfType.size() == 1) { - return beansOfType.values().iterator().next(); + return uniqueBean(type, beansOfType); + } + + /** + * Extract a unique bean for the given type from the given Map of matching beans. + * @param type type of bean to match + * @param matchingBeans all matching beans found + * @return the unique bean instance + * @throws NoSuchBeanDefinitionException if no bean of the given type was found + * @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found + */ + private static T uniqueBean(Class type, Map matchingBeans) { + int nrFound = matchingBeans.size(); + if (nrFound == 1) { + return matchingBeans.values().iterator().next(); + } + else if (nrFound > 1) { + throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet()); } else { - throw new NoSuchBeanDefinitionException(type, "expected single bean but found " + beansOfType.size()); + throw new NoSuchBeanDefinitionException(type); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/NoSuchBeanDefinitionException.java b/spring-beans/src/main/java/org/springframework/beans/factory/NoSuchBeanDefinitionException.java index 0157d08650..5bd8ea7a9f 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/NoSuchBeanDefinitionException.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/NoSuchBeanDefinitionException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -20,11 +20,13 @@ import org.springframework.beans.BeansException; import org.springframework.util.StringUtils; /** - * Exception thrown when a {@code BeanFactory} is asked for a bean - * instance for which it cannot find a definition. + * Exception thrown when a {@code BeanFactory} is asked for a bean instance + * for which it cannot find a definition. * * @author Rod Johnson * @author Juergen Hoeller + * @see BeanFactory#getBean(String) + * @see BeanFactory#getBean(Class) */ @SuppressWarnings("serial") public class NoSuchBeanDefinitionException extends BeansException { @@ -60,7 +62,7 @@ public class NoSuchBeanDefinitionException extends BeansException { * @param type required type of the missing bean */ public NoSuchBeanDefinitionException(Class type) { - super("No unique bean of type [" + type.getName() + "] is defined"); + super("No qualifying bean of type [" + type.getName() + "] is defined"); this.beanType = type; } @@ -70,7 +72,7 @@ public class NoSuchBeanDefinitionException extends BeansException { * @param message detailed message describing the problem */ public NoSuchBeanDefinitionException(Class type, String message) { - super("No unique bean of type [" + type.getName() + "] is defined: " + message); + super("No qualifying bean of type [" + type.getName() + "] is defined: " + message); this.beanType = type; } @@ -81,7 +83,7 @@ public class NoSuchBeanDefinitionException extends BeansException { * @param message detailed message describing the problem */ public NoSuchBeanDefinitionException(Class type, String dependencyDescription, String message) { - super("No matching bean of type [" + type.getName() + "] found for dependency" + + super("No qualifying bean of type [" + type.getName() + "] found for dependency" + (StringUtils.hasLength(dependencyDescription) ? " [" + dependencyDescription + "]" : "") + ": " + message); this.beanType = type; @@ -89,19 +91,26 @@ public class NoSuchBeanDefinitionException extends BeansException { /** - * Return the name of the missing bean, if it was a lookup by name - * that failed. + * Return the name of the missing bean, if it was a lookup by name that failed. */ public String getBeanName() { return this.beanName; } /** - * Return the required type of the missing bean, if it was a lookup - * by type that failed. + * Return the required type of the missing bean, if it was a lookup by type that failed. */ public Class getBeanType() { return this.beanType; } + /** + * Return the number of beans found when only one matching bean was expected. + * For a regular NoSuchBeanDefinitionException, this will always be 0. + * @see NoUniqueBeanDefinitionException + */ + public int getNumberOfBeansFound() { + return 0; + } + } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/NoUniqueBeanDefinitionException.java b/spring-beans/src/main/java/org/springframework/beans/factory/NoUniqueBeanDefinitionException.java new file mode 100644 index 0000000000..7db0bae6e1 --- /dev/null +++ b/spring-beans/src/main/java/org/springframework/beans/factory/NoUniqueBeanDefinitionException.java @@ -0,0 +1,79 @@ +/* + * Copyright 2002-2013 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.beans.factory; + +import java.util.Arrays; +import java.util.Collection; + +import org.springframework.util.StringUtils; + +/** + * Exception thrown when a {@code BeanFactory} is asked for a bean instance for which + * multiple matching candidates have been found when only one matching bean was expected. + * + * @author Juergen Hoeller + * @since 3.2.1 + * @see BeanFactory#getBean(Class) + */ +@SuppressWarnings("serial") +public class NoUniqueBeanDefinitionException extends NoSuchBeanDefinitionException { + + private int numberOfBeansFound; + + + /** + * Create a new {@code NoUniqueBeanDefinitionException}. + * @param type required type of the non-unique bean + * @param numberOfBeansFound the number of matching beans + * @param message detailed message describing the problem + */ + public NoUniqueBeanDefinitionException(Class type, int numberOfBeansFound, String message) { + super(type, message); + this.numberOfBeansFound = numberOfBeansFound; + } + + /** + * Create a new {@code NoUniqueBeanDefinitionException}. + * @param type required type of the non-unique bean + * @param beanNamesFound the names of all matching beans (as a Collection) + */ + public NoUniqueBeanDefinitionException(Class type, Collection beanNamesFound) { + this(type, beanNamesFound.size(), "expected single matching bean but found " + beanNamesFound.size() + ": " + + StringUtils.collectionToCommaDelimitedString(beanNamesFound)); + } + + /** + * Create a new {@code NoUniqueBeanDefinitionException}. + * @param type required type of the non-unique bean + * @param beanNamesFound the names of all matching beans (as an array) + */ + public NoUniqueBeanDefinitionException(Class type, String... beanNamesFound) { + this(type, Arrays.asList(beanNamesFound)); + } + + + /** + * Return the number of beans found when only one matching bean was expected. + * For a NoUniqueBeanDefinitionException, this will usually be higher than 1. + * @see #getBeanType() + */ + @Override + public int getNumberOfBeansFound() { + return this.numberOfBeansFound; + } + +} 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 824caaaec5..432663ab79 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -50,6 +50,7 @@ import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.CannotLoadBeanClassException; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.SmartFactoryBean; import org.springframework.beans.factory.config.BeanDefinition; @@ -270,12 +271,14 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto if (beanNames.length == 1) { return getBean(beanNames[0], requiredType); } - else if (beanNames.length == 0 && getParentBeanFactory() != null) { + else if (beanNames.length > 1) { + throw new NoUniqueBeanDefinitionException(requiredType, beanNames); + } + else if (getParentBeanFactory() != null) { return getParentBeanFactory().getBean(requiredType); } else { - throw new NoSuchBeanDefinitionException(requiredType, "expected single bean but found " + - beanNames.length + ": " + StringUtils.arrayToCommaDelimitedString(beanNames)); + throw new NoSuchBeanDefinitionException(requiredType); } } @@ -823,8 +826,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto if (matchingBeans.size() > 1) { String primaryBeanName = determinePrimaryCandidate(matchingBeans, descriptor); if (primaryBeanName == null) { - throw new NoSuchBeanDefinitionException(type, "expected single matching bean but found " + - matchingBeans.size() + ": " + matchingBeans.keySet()); + throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet()); } if (autowiredBeanNames != null) { autowiredBeanNames.add(primaryBeanName); @@ -895,7 +897,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto boolean candidateLocal = containsBeanDefinition(candidateBeanName); boolean primaryLocal = containsBeanDefinition(primaryBeanName); if (candidateLocal == primaryLocal) { - throw new NoSuchBeanDefinitionException(descriptor.getDependencyType(), + throw new NoUniqueBeanDefinitionException(descriptor.getDependencyType(), candidateBeans.size(), "more than one 'primary' bean found among candidates: " + candidateBeans.keySet()); } else if (candidateLocal && !primaryLocal) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java index 3e8784b181..4eef1b4751 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2013 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. @@ -31,6 +31,7 @@ import org.springframework.beans.factory.BeanNotOfRequiredTypeException; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.beans.factory.SmartFactoryBean; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.StringUtils; @@ -117,8 +118,11 @@ public class StaticListableBeanFactory implements ListableBeanFactory { if (beanNames.length == 1) { return getBean(beanNames[0], requiredType); } + else if (beanNames.length > 1) { + throw new NoUniqueBeanDefinitionException(requiredType, beanNames); + } else { - throw new NoSuchBeanDefinitionException(requiredType, "expected single bean but found " + beanNames.length); + throw new NoSuchBeanDefinitionException(requiredType); } } 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 acf9f65a05..e93e95973d 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 @@ -1270,6 +1270,12 @@ public class DefaultListableBeanFactoryTests { } @Test(expected=NoSuchBeanDefinitionException.class) + public void testGetBeanByTypeWithNoneFound() { + DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(); + lbf.getBean(TestBean.class); + } + + @Test(expected=NoUniqueBeanDefinitionException.class) public void testGetBeanByTypeWithAmbiguity() { DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(); RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class); @@ -1296,7 +1302,8 @@ public class DefaultListableBeanFactoryTests { try { lbf.getBean(TestBean.class); fail("Should have thrown NoSuchBeanDefinitionException"); - } catch (NoSuchBeanDefinitionException ex) { + } + catch (NoSuchBeanDefinitionException ex) { // expected } } diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java b/spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java index ac9c408f02..39c437e8cd 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -43,6 +43,7 @@ import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.beans.factory.annotation.InjectionMetadata; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor; @@ -60,7 +61,6 @@ import org.springframework.orm.jpa.ExtendedEntityManagerCreator; import org.springframework.orm.jpa.SharedEntityManagerCreator; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; -import org.springframework.util.StringUtils; /** * BeanPostProcessor that processes {@link javax.persistence.PersistenceUnit} @@ -534,10 +534,11 @@ public class PersistenceAnnotationBeanPostProcessor } return emf; } + else if (beanNames.length > 1) { + throw new NoUniqueBeanDefinitionException(EntityManagerFactory.class, beanNames); + } else { - throw new NoSuchBeanDefinitionException( - EntityManagerFactory.class, "expected single bean but found " + beanNames.length + ": " + - StringUtils.arrayToCommaDelimitedString(beanNames)); + throw new NoSuchBeanDefinitionException(EntityManagerFactory.class); } }