From a4b00c732b13aa1628161cb35d49463c5c39d38c Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Fri, 25 May 2012 17:23:01 +0300 Subject: [PATCH] Introduce BeanFactoryAnnotationUtils Commit 096693c46fba6e09b346a498b7002abd4d6540a9 refactored and deprecated TransactionAspectUtils, moving its #qualifiedBeanOfType and related methods into BeanFactoryUtils. This created a package cycle between beans.factory and beans.factory.annotation due to use of the beans.factory.annotation.Qualifier annotation in these methods. This commit breaks the package cycle by introducing beans.factory.annotation.BeanFactoryAnnotationUtils and moving these @Qualifier-related methods to it. It is intentionally similar in name and style to the familiar BeanFactoryUtils class for purposes of discoverability. There are no backward-compatibilty concerns associated with this change as the cycle was introduced, caught and now fixed before a release. Issue: SPR-6847 --- .../AsyncExecutionAspectSupport.java | 5 +- .../beans/factory/BeanFactoryUtils.java | 106 ------------- .../BeanFactoryAnnotationUtils.java | 141 ++++++++++++++++++ .../TransactionalTestExecutionListener.java | 4 +- .../interceptor/TransactionAspectSupport.java | 3 +- .../interceptor/TransactionAspectUtils.java | 9 +- 6 files changed, 153 insertions(+), 115 deletions(-) create mode 100644 spring-beans/src/main/java/org/springframework/beans/factory/annotation/BeanFactoryAnnotationUtils.java diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionAspectSupport.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionAspectSupport.java index f1bc5cf856..4ff0e9244b 100644 --- a/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionAspectSupport.java +++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionAspectSupport.java @@ -25,7 +25,7 @@ import java.util.concurrent.Executor; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.BeanFactoryUtils; +import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; import org.springframework.core.task.AsyncTaskExecutor; import org.springframework.core.task.support.TaskExecutorAdapter; import org.springframework.util.Assert; @@ -110,7 +110,8 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware { Assert.notNull(this.beanFactory, "BeanFactory must be set on " + this.getClass().getSimpleName() + " to access qualified executor [" + qualifier + "]"); - executor = BeanFactoryUtils.qualifiedBeanOfType(this.beanFactory, Executor.class, qualifier); + executor = BeanFactoryAnnotationUtils.qualifiedBeanOfType( + this.beanFactory, Executor.class, qualifier); } if (executor instanceof AsyncTaskExecutor) { 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 4b0410040a..0e58200ad9 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 @@ -16,7 +16,6 @@ package org.springframework.beans.factory; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; @@ -25,14 +24,7 @@ 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; /** @@ -441,102 +433,4 @@ 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-beans/src/main/java/org/springframework/beans/factory/annotation/BeanFactoryAnnotationUtils.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/BeanFactoryAnnotationUtils.java new file mode 100644 index 0000000000..878e4907dc --- /dev/null +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/BeanFactoryAnnotationUtils.java @@ -0,0 +1,141 @@ +/* + * 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. + * 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.annotation; + +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.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.ObjectUtils; + +/** + * Convenience methods performing bean lookups related to annotations, for example + * Spring's {@link Qualifier @Qualifier} annotation. + * + * @author Chris Beams + * @since 3.2 + * @see BeanFactoryUtils + */ +public class BeanFactoryAnnotationUtils { + + /** + * 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 da32371e96..2e533ad463 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 @@ -30,7 +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.beans.factory.annotation.BeanFactoryAnnotationUtils; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.test.annotation.NotTransactional; import org.springframework.test.annotation.Rollback; @@ -155,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 = BeanFactoryUtils.qualifiedBeanOfType(bf, PlatformTransactionManager.class, qualifier); + tm = BeanFactoryAnnotationUtils.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 ec4348e5c2..049ca612f4 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 @@ -27,6 +27,7 @@ import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.ListableBeanFactory; +import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; import org.springframework.core.NamedThreadLocal; import org.springframework.transaction.NoTransactionException; import org.springframework.transaction.PlatformTransactionManager; @@ -242,7 +243,7 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init } String qualifier = txAttr.getQualifier(); if (StringUtils.hasLength(qualifier)) { - return BeanFactoryUtils.qualifiedBeanOfType(this.beanFactory, PlatformTransactionManager.class, qualifier); + return BeanFactoryAnnotationUtils.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 5b9bd14824..703f4a0bb2 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 @@ -18,6 +18,7 @@ package org.springframework.transaction.interceptor; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryUtils; +import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.transaction.PlatformTransactionManager; @@ -40,10 +41,10 @@ public abstract class TransactionAspectUtils { * @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)} + * {@link BeanFactoryAnnotationUtils#qualifiedBeanOfType(BeanFactory, Class, String)} */ public static PlatformTransactionManager getTransactionManager(BeanFactory beanFactory, String qualifier) { - return BeanFactoryUtils.qualifiedBeanOfType(beanFactory, PlatformTransactionManager.class, qualifier); + return BeanFactoryAnnotationUtils.qualifiedBeanOfType(beanFactory, PlatformTransactionManager.class, qualifier); } /** @@ -53,10 +54,10 @@ public abstract class TransactionAspectUtils { * @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)} + * {@link BeanFactoryAnnotationUtils#qualifiedBeanOfType(BeanFactory, Class, String)} */ public static PlatformTransactionManager getTransactionManager(ConfigurableListableBeanFactory bf, String qualifier) { - return BeanFactoryUtils.qualifiedBeanOfType(bf, PlatformTransactionManager.class, qualifier); + return BeanFactoryAnnotationUtils.qualifiedBeanOfType(bf, PlatformTransactionManager.class, qualifier); } }