Skip to content

Commit 096693c

Browse files
committed
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.
1 parent e71cd06 commit 096693c

File tree

4 files changed

+135
-99
lines changed

4 files changed

+135
-99
lines changed

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

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2010 the original author or authors.
2+
* Copyright 2002-2012 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.
@@ -16,14 +16,23 @@
1616

1717
package org.springframework.beans.factory;
1818

19+
import java.lang.reflect.Method;
20+
1921
import java.util.ArrayList;
2022
import java.util.Arrays;
2123
import java.util.LinkedHashMap;
2224
import java.util.List;
2325
import java.util.Map;
2426

2527
import org.springframework.beans.BeansException;
28+
import org.springframework.beans.factory.annotation.Qualifier;
29+
import org.springframework.beans.factory.config.BeanDefinition;
30+
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
31+
import org.springframework.beans.factory.support.AbstractBeanDefinition;
32+
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
33+
import org.springframework.beans.factory.support.RootBeanDefinition;
2634
import org.springframework.util.Assert;
35+
import org.springframework.util.ObjectUtils;
2736
import org.springframework.util.StringUtils;
2837

2938
/**
@@ -37,6 +46,7 @@
3746
*
3847
* @author Rod Johnson
3948
* @author Juergen Hoeller
49+
* @author Chris Beams
4050
* @since 04.07.2003
4151
*/
4252
public abstract class BeanFactoryUtils {
@@ -431,4 +441,102 @@ public static <T> T beanOfType(
431441
}
432442
}
433443

444+
/**
445+
* Obtain a bean of type {@code T} from the given {@code BeanFactory} declaring a
446+
* qualifier (e.g. via {@code <qualifier>} or {@code @Qualifier}) matching the given
447+
* qualifier, or having a bean name matching the given qualifier.
448+
* @param bf the BeanFactory to get the target bean from
449+
* @param beanType the type of bean to retrieve
450+
* @param qualifier the qualifier for selecting between multiple bean matches
451+
* @return the matching bean of type {@code T} (never {@code null})
452+
* @throws IllegalStateException if no matching bean of type {@code T} found
453+
* @since 3.2
454+
*/
455+
public static <T> T qualifiedBeanOfType(BeanFactory beanFactory, Class<T> beanType, String qualifier) {
456+
if (beanFactory instanceof ConfigurableListableBeanFactory) {
457+
// Full qualifier matching supported.
458+
return qualifiedBeanOfType((ConfigurableListableBeanFactory) beanFactory, beanType, qualifier);
459+
}
460+
else if (beanFactory.containsBean(qualifier)) {
461+
// Fallback: target bean at least found by bean name.
462+
return beanFactory.getBean(qualifier, beanType);
463+
}
464+
else {
465+
throw new IllegalStateException("No matching " + beanType.getSimpleName() +
466+
" bean found for bean name '" + qualifier +
467+
"'! (Note: Qualifier matching not supported because given " +
468+
"BeanFactory does not implement ConfigurableListableBeanFactory.)");
469+
}
470+
}
471+
472+
/**
473+
* Obtain a bean of type {@code T} from the given {@code BeanFactory} declaring a
474+
* qualifier (e.g. {@code <qualifier>} or {@code @Qualifier}) matching the given
475+
* qualifier
476+
* @param bf the BeanFactory to get the target bean from
477+
* @param beanType the type of bean to retrieve
478+
* @param qualifier the qualifier for selecting between multiple bean matches
479+
* @return the matching bean of type {@code T} (never {@code null})
480+
* @throws IllegalStateException if no matching bean of type {@code T} found
481+
*/
482+
private static <T> T qualifiedBeanOfType(ConfigurableListableBeanFactory bf, Class<T> beanType, String qualifier) {
483+
Map<String, T> candidateBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(bf, beanType);
484+
T matchingBean = null;
485+
for (String beanName : candidateBeans.keySet()) {
486+
if (isQualifierMatch(qualifier, beanName, bf)) {
487+
if (matchingBean != null) {
488+
throw new IllegalStateException("No unique " + beanType.getSimpleName() +
489+
" bean found for qualifier '" + qualifier + "'");
490+
}
491+
matchingBean = candidateBeans.get(beanName);
492+
}
493+
}
494+
if (matchingBean != null) {
495+
return matchingBean;
496+
}
497+
else {
498+
throw new IllegalStateException("No matching " + beanType.getSimpleName() +
499+
" bean found for qualifier '" + qualifier + "' - neither qualifier " +
500+
"match nor bean name match!");
501+
}
502+
}
503+
504+
/**
505+
* Check whether the named bean declares a qualifier of the given name.
506+
* @param qualifier the qualifier to match
507+
* @param beanName the name of the candidate bean
508+
* @param bf the {@code BeanFactory} from which to retrieve the named bean
509+
* @return {@code true} if either the bean definition (in the XML case)
510+
* or the bean's factory method (in the {@code @Bean} case) defines a matching
511+
* qualifier value (through {@code <qualifier>} or {@code @Qualifier})
512+
*/
513+
private static boolean isQualifierMatch(String qualifier, String beanName, ConfigurableListableBeanFactory bf) {
514+
if (bf.containsBean(beanName)) {
515+
try {
516+
BeanDefinition bd = bf.getMergedBeanDefinition(beanName);
517+
if (bd instanceof AbstractBeanDefinition) {
518+
AbstractBeanDefinition abd = (AbstractBeanDefinition) bd;
519+
AutowireCandidateQualifier candidate = abd.getQualifier(Qualifier.class.getName());
520+
if ((candidate != null && qualifier.equals(candidate.getAttribute(AutowireCandidateQualifier.VALUE_KEY))) ||
521+
qualifier.equals(beanName) || ObjectUtils.containsElement(bf.getAliases(beanName), qualifier)) {
522+
return true;
523+
}
524+
}
525+
if (bd instanceof RootBeanDefinition) {
526+
Method factoryMethod = ((RootBeanDefinition) bd).getResolvedFactoryMethod();
527+
if (factoryMethod != null) {
528+
Qualifier targetAnnotation = factoryMethod.getAnnotation(Qualifier.class);
529+
if (targetAnnotation != null && qualifier.equals(targetAnnotation.value())) {
530+
return true;
531+
}
532+
}
533+
}
534+
}
535+
catch (NoSuchBeanDefinitionException ex) {
536+
// ignore - can't compare qualifiers for a manually registered singleton object
537+
}
538+
}
539+
return false;
540+
}
541+
434542
}

spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2011 the original author or authors.
2+
* Copyright 2002-2012 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.
@@ -19,6 +19,7 @@
1919
import java.lang.annotation.Annotation;
2020
import java.lang.reflect.InvocationTargetException;
2121
import java.lang.reflect.Method;
22+
2223
import java.util.ArrayList;
2324
import java.util.Collections;
2425
import java.util.IdentityHashMap;
@@ -29,6 +30,7 @@
2930
import org.apache.commons.logging.LogFactory;
3031
import org.springframework.beans.BeansException;
3132
import org.springframework.beans.factory.BeanFactory;
33+
import org.springframework.beans.factory.BeanFactoryUtils;
3234
import org.springframework.core.annotation.AnnotationUtils;
3335
import org.springframework.test.annotation.NotTransactional;
3436
import org.springframework.test.annotation.Rollback;
@@ -40,7 +42,6 @@
4042
import org.springframework.transaction.TransactionStatus;
4143
import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
4244
import org.springframework.transaction.interceptor.DelegatingTransactionAttribute;
43-
import org.springframework.transaction.interceptor.TransactionAspectUtils;
4445
import org.springframework.transaction.interceptor.TransactionAttribute;
4546
import org.springframework.transaction.interceptor.TransactionAttributeSource;
4647
import org.springframework.util.Assert;
@@ -154,7 +155,7 @@ public String getName() {
154155
// qualifier matching (only exposed on the internal BeanFactory,
155156
// not on the ApplicationContext).
156157
BeanFactory bf = testContext.getApplicationContext().getAutowireCapableBeanFactory();
157-
tm = TransactionAspectUtils.getTransactionManager(bf, qualifier);
158+
tm = BeanFactoryUtils.qualifiedBeanOfType(bf, PlatformTransactionManager.class, qualifier);
158159
}
159160
else {
160161
tm = getTransactionManager(testContext);

spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2010 the original author or authors.
2+
* Copyright 2002-2012 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.
@@ -242,7 +242,7 @@ protected PlatformTransactionManager determineTransactionManager(TransactionAttr
242242
}
243243
String qualifier = txAttr.getQualifier();
244244
if (StringUtils.hasLength(qualifier)) {
245-
return TransactionAspectUtils.getTransactionManager(this.beanFactory, qualifier);
245+
return BeanFactoryUtils.qualifiedBeanOfType(this.beanFactory, PlatformTransactionManager.class, qualifier);
246246
}
247247
else if (this.transactionManagerBeanName != null) {
248248
return this.beanFactory.getBean(this.transactionManagerBeanName, PlatformTransactionManager.class);
Lines changed: 20 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2011 the original author or authors.
2+
* Copyright 2002-2012 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.
@@ -16,120 +16,47 @@
1616

1717
package org.springframework.transaction.interceptor;
1818

19-
import java.lang.reflect.Method;
20-
import java.util.Map;
21-
2219
import org.springframework.beans.factory.BeanFactory;
2320
import org.springframework.beans.factory.BeanFactoryUtils;
24-
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
25-
import org.springframework.beans.factory.annotation.Qualifier;
26-
import org.springframework.beans.factory.config.BeanDefinition;
2721
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
28-
import org.springframework.beans.factory.support.AbstractBeanDefinition;
29-
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
30-
import org.springframework.beans.factory.support.RootBeanDefinition;
3122
import org.springframework.transaction.PlatformTransactionManager;
32-
import org.springframework.util.ObjectUtils;
3323

3424
/**
3525
* Utility methods for obtaining a PlatformTransactionManager by
3626
* {@link TransactionAttribute#getQualifier() qualifier value}.
3727
*
3828
* @author Juergen Hoeller
29+
* @author Chris Beams
3930
* @since 3.0.2
31+
* @deprecated as of Spring 3.2 in favor of {@link BeanFactoryUtils}
4032
*/
33+
@Deprecated
4134
public abstract class TransactionAspectUtils {
4235

4336
/**
44-
* Obtain a PlatformTransactionManager from the given BeanFactory,
45-
* matching the given qualifier.
46-
* @param beanFactory the BeanFactory to get the PlatformTransactionManager bean from
47-
* @param qualifier the qualifier for selecting between multiple PlatformTransactionManager matches
48-
* @return the chosen PlatformTransactionManager (never <code>null</code>)
49-
* @throws IllegalStateException if no matching PlatformTransactionManager bean found
37+
* Obtain a PlatformTransactionManager from the given BeanFactory, matching the given qualifier.
38+
* @param beanFactory the BeanFactory to get the {@code PlatformTransactionManager} bean from
39+
* @param qualifier the qualifier for selecting between multiple {@code PlatformTransactionManager} matches
40+
* @return the chosen {@code PlatformTransactionManager} (never {@code null})
41+
* @throws IllegalStateException if no matching {@code PlatformTransactionManager} bean found
42+
* @deprecated as of Spring 3.2 in favor of
43+
* {@link BeanFactoryUtils#qualifiedBeanOfType(BeanFactory, Class, String)}
5044
*/
5145
public static PlatformTransactionManager getTransactionManager(BeanFactory beanFactory, String qualifier) {
52-
if (beanFactory instanceof ConfigurableListableBeanFactory) {
53-
// Full qualifier matching supported.
54-
return getTransactionManager((ConfigurableListableBeanFactory) beanFactory, qualifier);
55-
}
56-
else if (beanFactory.containsBean(qualifier)) {
57-
// Fallback: PlatformTransactionManager at least found by bean name.
58-
return beanFactory.getBean(qualifier, PlatformTransactionManager.class);
59-
}
60-
else {
61-
throw new IllegalStateException("No matching PlatformTransactionManager bean found for bean name '" +
62-
qualifier + "'! (Note: Qualifier matching not supported because given BeanFactory does not " +
63-
"implement ConfigurableListableBeanFactory.)");
64-
}
46+
return BeanFactoryUtils.qualifiedBeanOfType(beanFactory, PlatformTransactionManager.class, qualifier);
6547
}
6648

6749
/**
68-
* Obtain a PlatformTransactionManager from the given BeanFactory,
69-
* matching the given qualifier.
70-
* @param bf the BeanFactory to get the PlatformTransactionManager bean from
71-
* @param qualifier the qualifier for selecting between multiple PlatformTransactionManager matches
72-
* @return the chosen PlatformTransactionManager (never <code>null</code>)
73-
* @throws IllegalStateException if no matching PlatformTransactionManager bean found
50+
* Obtain a PlatformTransactionManager from the given BeanFactory, matching the given qualifier.
51+
* @param bf the BeanFactory to get the {@code PlatformTransactionManager} bean from
52+
* @param qualifier the qualifier for selecting between multiple {@code PlatformTransactionManager} matches
53+
* @return the chosen {@code PlatformTransactionManager} (never {@code null})
54+
* @throws IllegalStateException if no matching {@code PlatformTransactionManager} bean found
55+
* @deprecated as of Spring 3.2 in favor of
56+
* {@link BeanFactoryUtils#qualifiedBeanOfType(BeanFactory, Class, String)}
7457
*/
7558
public static PlatformTransactionManager getTransactionManager(ConfigurableListableBeanFactory bf, String qualifier) {
76-
Map<String, PlatformTransactionManager> tms =
77-
BeanFactoryUtils.beansOfTypeIncludingAncestors(bf, PlatformTransactionManager.class);
78-
PlatformTransactionManager chosen = null;
79-
for (String beanName : tms.keySet()) {
80-
if (isQualifierMatch(qualifier, beanName, bf)) {
81-
if (chosen != null) {
82-
throw new IllegalStateException("No unique PlatformTransactionManager bean found " +
83-
"for qualifier '" + qualifier + "'");
84-
}
85-
chosen = tms.get(beanName);
86-
}
87-
}
88-
if (chosen != null) {
89-
return chosen;
90-
}
91-
else {
92-
throw new IllegalStateException("No matching PlatformTransactionManager bean found for qualifier '" +
93-
qualifier + "' - neither qualifier match nor bean name match!");
94-
}
95-
}
96-
97-
/**
98-
* Check whether we have a qualifier match for the given candidate bean.
99-
* @param qualifier the qualifier that we are looking for
100-
* @param beanName the name of the candidate bean
101-
* @param bf the BeanFactory to get the bean definition from
102-
* @return <code>true</code> if either the bean definition (in the XML case)
103-
* or the bean's factory method (in the @Bean case) defines a matching qualifier
104-
* value (through &lt;qualifier<&gt; or @Qualifier)
105-
*/
106-
private static boolean isQualifierMatch(String qualifier, String beanName, ConfigurableListableBeanFactory bf) {
107-
if (bf.containsBean(beanName)) {
108-
try {
109-
BeanDefinition bd = bf.getMergedBeanDefinition(beanName);
110-
if (bd instanceof AbstractBeanDefinition) {
111-
AbstractBeanDefinition abd = (AbstractBeanDefinition) bd;
112-
AutowireCandidateQualifier candidate = abd.getQualifier(Qualifier.class.getName());
113-
if ((candidate != null && qualifier.equals(candidate.getAttribute(AutowireCandidateQualifier.VALUE_KEY))) ||
114-
qualifier.equals(beanName) || ObjectUtils.containsElement(bf.getAliases(beanName), qualifier)) {
115-
return true;
116-
}
117-
}
118-
if (bd instanceof RootBeanDefinition) {
119-
Method factoryMethod = ((RootBeanDefinition) bd).getResolvedFactoryMethod();
120-
if (factoryMethod != null) {
121-
Qualifier targetAnnotation = factoryMethod.getAnnotation(Qualifier.class);
122-
if (targetAnnotation != null && qualifier.equals(targetAnnotation.value())) {
123-
return true;
124-
}
125-
}
126-
}
127-
}
128-
catch (NoSuchBeanDefinitionException ex) {
129-
// ignore - can't compare qualifiers for a manually registered singleton object
130-
}
131-
}
132-
return false;
59+
return BeanFactoryUtils.qualifiedBeanOfType(bf, PlatformTransactionManager.class, qualifier);
13360
}
13461

13562
}

0 commit comments

Comments
 (0)