From 096693c46fba6e09b346a498b7002abd4d6540a9 Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Sat, 19 May 2012 19:30:58 +0300 Subject: [PATCH] Refactor and deprecate TransactionAspectUtils TransactionAspectUtils contains a number of methods useful in retrieving a bean by type+qualifier. These methods are functionally general-purpose save for the hard coding of PlatformTransactionManager class literals throughout. This commit generifies these methods and moves them into BeanFactoryUtils primarily in anticipation of their use by async method execution interceptors and aspects when performing lookups for qualified executor beans e.g. via @Async("qualifier"). The public API of TransactionAspectUtils remains backward compatible; all methods within have been deprecated, and all calls to those methods throughout the framework refactored to use the new BeanFactoryUtils variants instead. --- .../beans/factory/BeanFactoryUtils.java | 110 ++++++++++++++++- .../TransactionalTestExecutionListener.java | 7 +- .../interceptor/TransactionAspectSupport.java | 4 +- .../interceptor/TransactionAspectUtils.java | 113 ++++-------------- 4 files changed, 135 insertions(+), 99 deletions(-) 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 5bd1787dde..4b0410040a 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 88c0ba5fae..da32371e96 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 5890e5bf20..ec4348e5c2 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 84b082a272..5b9bd14824 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); } }