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 5bd1787ddeb..4b0410040a8 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-2010 the original author or authors. + * Copyright 2002-2012 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. @@ -16,6 +16,8 @@ package org.springframework.beans.factory; +import java.lang.reflect.Method; + import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; @@ -23,7 +25,14 @@ import java.util.List; import java.util.Map; import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.AutowireCandidateQualifier; +import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; /** @@ -37,6 +46,7 @@ import org.springframework.util.StringUtils; * * @author Rod Johnson * @author Juergen Hoeller + * @author Chris Beams * @since 04.07.2003 */ public abstract class BeanFactoryUtils { @@ -431,4 +441,102 @@ public abstract class BeanFactoryUtils { } } + /** + * Obtain a bean of type {@code T} from the given {@code BeanFactory} declaring a + * qualifier (e.g. via {@code } or {@code @Qualifier}) matching the given + * qualifier, or having a bean name matching the given qualifier. + * @param bf the BeanFactory to get the target bean from + * @param beanType the type of bean to retrieve + * @param qualifier the qualifier for selecting between multiple bean matches + * @return the matching bean of type {@code T} (never {@code null}) + * @throws IllegalStateException if no matching bean of type {@code T} found + * @since 3.2 + */ + public static T qualifiedBeanOfType(BeanFactory beanFactory, Class beanType, String qualifier) { + if (beanFactory instanceof ConfigurableListableBeanFactory) { + // Full qualifier matching supported. + return qualifiedBeanOfType((ConfigurableListableBeanFactory) beanFactory, beanType, qualifier); + } + else if (beanFactory.containsBean(qualifier)) { + // Fallback: target bean at least found by bean name. + return beanFactory.getBean(qualifier, beanType); + } + else { + throw new IllegalStateException("No matching " + beanType.getSimpleName() + + " bean found for bean name '" + qualifier + + "'! (Note: Qualifier matching not supported because given " + + "BeanFactory does not implement ConfigurableListableBeanFactory.)"); + } + } + + /** + * Obtain a bean of type {@code T} from the given {@code BeanFactory} declaring a + * qualifier (e.g. {@code } or {@code @Qualifier}) matching the given + * qualifier + * @param bf the BeanFactory to get the target bean from + * @param beanType the type of bean to retrieve + * @param qualifier the qualifier for selecting between multiple bean matches + * @return the matching bean of type {@code T} (never {@code null}) + * @throws IllegalStateException if no matching bean of type {@code T} found + */ + private static T qualifiedBeanOfType(ConfigurableListableBeanFactory bf, Class beanType, String qualifier) { + Map candidateBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(bf, beanType); + T matchingBean = null; + for (String beanName : candidateBeans.keySet()) { + if (isQualifierMatch(qualifier, beanName, bf)) { + if (matchingBean != null) { + throw new IllegalStateException("No unique " + beanType.getSimpleName() + + " bean found for qualifier '" + qualifier + "'"); + } + matchingBean = candidateBeans.get(beanName); + } + } + if (matchingBean != null) { + return matchingBean; + } + else { + throw new IllegalStateException("No matching " + beanType.getSimpleName() + + " bean found for qualifier '" + qualifier + "' - neither qualifier " + + "match nor bean name match!"); + } + } + + /** + * Check whether the named bean declares a qualifier of the given name. + * @param qualifier the qualifier to match + * @param beanName the name of the candidate bean + * @param bf the {@code BeanFactory} from which to retrieve the named bean + * @return {@code true} if either the bean definition (in the XML case) + * or the bean's factory method (in the {@code @Bean} case) defines a matching + * qualifier value (through {@code } or {@code @Qualifier}) + */ + private static boolean isQualifierMatch(String qualifier, String beanName, ConfigurableListableBeanFactory bf) { + if (bf.containsBean(beanName)) { + try { + BeanDefinition bd = bf.getMergedBeanDefinition(beanName); + if (bd instanceof AbstractBeanDefinition) { + AbstractBeanDefinition abd = (AbstractBeanDefinition) bd; + AutowireCandidateQualifier candidate = abd.getQualifier(Qualifier.class.getName()); + if ((candidate != null && qualifier.equals(candidate.getAttribute(AutowireCandidateQualifier.VALUE_KEY))) || + qualifier.equals(beanName) || ObjectUtils.containsElement(bf.getAliases(beanName), qualifier)) { + return true; + } + } + if (bd instanceof RootBeanDefinition) { + Method factoryMethod = ((RootBeanDefinition) bd).getResolvedFactoryMethod(); + if (factoryMethod != null) { + Qualifier targetAnnotation = factoryMethod.getAnnotation(Qualifier.class); + if (targetAnnotation != null && qualifier.equals(targetAnnotation.value())) { + return true; + } + } + } + } + catch (NoSuchBeanDefinitionException ex) { + // ignore - can't compare qualifiers for a manually registered singleton object + } + } + return false; + } + } diff --git a/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java index 88c0ba5faee..da32371e96b 100644 --- a/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java +++ b/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -19,6 +19,7 @@ package org.springframework.test.context.transaction; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; + import java.util.ArrayList; import java.util.Collections; import java.util.IdentityHashMap; @@ -29,6 +30,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.test.annotation.NotTransactional; import org.springframework.test.annotation.Rollback; @@ -40,7 +42,6 @@ import org.springframework.transaction.TransactionException; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource; import org.springframework.transaction.interceptor.DelegatingTransactionAttribute; -import org.springframework.transaction.interceptor.TransactionAspectUtils; import org.springframework.transaction.interceptor.TransactionAttribute; import org.springframework.transaction.interceptor.TransactionAttributeSource; import org.springframework.util.Assert; @@ -154,7 +155,7 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis // qualifier matching (only exposed on the internal BeanFactory, // not on the ApplicationContext). BeanFactory bf = testContext.getApplicationContext().getAutowireCapableBeanFactory(); - tm = TransactionAspectUtils.getTransactionManager(bf, qualifier); + tm = BeanFactoryUtils.qualifiedBeanOfType(bf, PlatformTransactionManager.class, qualifier); } else { tm = getTransactionManager(testContext); diff --git a/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java b/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java index 5890e5bf202..ec4348e5c29 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java +++ b/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2012 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. @@ -242,7 +242,7 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init } String qualifier = txAttr.getQualifier(); if (StringUtils.hasLength(qualifier)) { - return TransactionAspectUtils.getTransactionManager(this.beanFactory, qualifier); + return BeanFactoryUtils.qualifiedBeanOfType(this.beanFactory, PlatformTransactionManager.class, qualifier); } else if (this.transactionManagerBeanName != null) { return this.beanFactory.getBean(this.transactionManagerBeanName, PlatformTransactionManager.class); diff --git a/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectUtils.java b/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectUtils.java index 84b082a2720..5b9bd148243 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectUtils.java +++ b/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -16,120 +16,47 @@ package org.springframework.transaction.interceptor; -import java.lang.reflect.Method; -import java.util.Map; - import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryUtils; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.AbstractBeanDefinition; -import org.springframework.beans.factory.support.AutowireCandidateQualifier; -import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.util.ObjectUtils; /** * Utility methods for obtaining a PlatformTransactionManager by * {@link TransactionAttribute#getQualifier() qualifier value}. * * @author Juergen Hoeller + * @author Chris Beams * @since 3.0.2 + * @deprecated as of Spring 3.2 in favor of {@link BeanFactoryUtils} */ +@Deprecated public abstract class TransactionAspectUtils { /** - * Obtain a PlatformTransactionManager from the given BeanFactory, - * matching the given qualifier. - * @param beanFactory the BeanFactory to get the PlatformTransactionManager bean from - * @param qualifier the qualifier for selecting between multiple PlatformTransactionManager matches - * @return the chosen PlatformTransactionManager (never null) - * @throws IllegalStateException if no matching PlatformTransactionManager bean found + * Obtain a PlatformTransactionManager from the given BeanFactory, matching the given qualifier. + * @param beanFactory the BeanFactory to get the {@code PlatformTransactionManager} bean from + * @param qualifier the qualifier for selecting between multiple {@code PlatformTransactionManager} matches + * @return the chosen {@code PlatformTransactionManager} (never {@code null}) + * @throws IllegalStateException if no matching {@code PlatformTransactionManager} bean found + * @deprecated as of Spring 3.2 in favor of + * {@link BeanFactoryUtils#qualifiedBeanOfType(BeanFactory, Class, String)} */ public static PlatformTransactionManager getTransactionManager(BeanFactory beanFactory, String qualifier) { - if (beanFactory instanceof ConfigurableListableBeanFactory) { - // Full qualifier matching supported. - return getTransactionManager((ConfigurableListableBeanFactory) beanFactory, qualifier); - } - else if (beanFactory.containsBean(qualifier)) { - // Fallback: PlatformTransactionManager at least found by bean name. - return beanFactory.getBean(qualifier, PlatformTransactionManager.class); - } - else { - throw new IllegalStateException("No matching PlatformTransactionManager bean found for bean name '" + - qualifier + "'! (Note: Qualifier matching not supported because given BeanFactory does not " + - "implement ConfigurableListableBeanFactory.)"); - } + return BeanFactoryUtils.qualifiedBeanOfType(beanFactory, PlatformTransactionManager.class, qualifier); } /** - * Obtain a PlatformTransactionManager from the given BeanFactory, - * matching the given qualifier. - * @param bf the BeanFactory to get the PlatformTransactionManager bean from - * @param qualifier the qualifier for selecting between multiple PlatformTransactionManager matches - * @return the chosen PlatformTransactionManager (never null) - * @throws IllegalStateException if no matching PlatformTransactionManager bean found + * Obtain a PlatformTransactionManager from the given BeanFactory, matching the given qualifier. + * @param bf the BeanFactory to get the {@code PlatformTransactionManager} bean from + * @param qualifier the qualifier for selecting between multiple {@code PlatformTransactionManager} matches + * @return the chosen {@code PlatformTransactionManager} (never {@code null}) + * @throws IllegalStateException if no matching {@code PlatformTransactionManager} bean found + * @deprecated as of Spring 3.2 in favor of + * {@link BeanFactoryUtils#qualifiedBeanOfType(BeanFactory, Class, String)} */ public static PlatformTransactionManager getTransactionManager(ConfigurableListableBeanFactory bf, String qualifier) { - Map tms = - BeanFactoryUtils.beansOfTypeIncludingAncestors(bf, PlatformTransactionManager.class); - PlatformTransactionManager chosen = null; - for (String beanName : tms.keySet()) { - if (isQualifierMatch(qualifier, beanName, bf)) { - if (chosen != null) { - throw new IllegalStateException("No unique PlatformTransactionManager bean found " + - "for qualifier '" + qualifier + "'"); - } - chosen = tms.get(beanName); - } - } - if (chosen != null) { - return chosen; - } - else { - throw new IllegalStateException("No matching PlatformTransactionManager bean found for qualifier '" + - qualifier + "' - neither qualifier match nor bean name match!"); - } - } - - /** - * Check whether we have a qualifier match for the given candidate bean. - * @param qualifier the qualifier that we are looking for - * @param beanName the name of the candidate bean - * @param bf the BeanFactory to get the bean definition from - * @return true if either the bean definition (in the XML case) - * or the bean's factory method (in the @Bean case) defines a matching qualifier - * value (through <qualifier<> or @Qualifier) - */ - private static boolean isQualifierMatch(String qualifier, String beanName, ConfigurableListableBeanFactory bf) { - if (bf.containsBean(beanName)) { - try { - BeanDefinition bd = bf.getMergedBeanDefinition(beanName); - if (bd instanceof AbstractBeanDefinition) { - AbstractBeanDefinition abd = (AbstractBeanDefinition) bd; - AutowireCandidateQualifier candidate = abd.getQualifier(Qualifier.class.getName()); - if ((candidate != null && qualifier.equals(candidate.getAttribute(AutowireCandidateQualifier.VALUE_KEY))) || - qualifier.equals(beanName) || ObjectUtils.containsElement(bf.getAliases(beanName), qualifier)) { - return true; - } - } - if (bd instanceof RootBeanDefinition) { - Method factoryMethod = ((RootBeanDefinition) bd).getResolvedFactoryMethod(); - if (factoryMethod != null) { - Qualifier targetAnnotation = factoryMethod.getAnnotation(Qualifier.class); - if (targetAnnotation != null && qualifier.equals(targetAnnotation.value())) { - return true; - } - } - } - } - catch (NoSuchBeanDefinitionException ex) { - // ignore - can't compare qualifiers for a manually registered singleton object - } - } - return false; + return BeanFactoryUtils.qualifiedBeanOfType(bf, PlatformTransactionManager.class, qualifier); } }