Skip to content

Commit 214c919

Browse files
committed
AspectJ bean pointcut supports qualifier match
Issue: SPR-11217
1 parent e802f0e commit 214c919

File tree

4 files changed

+71
-55
lines changed

4 files changed

+71
-55
lines changed

spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJExpressionPointcut.java

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import org.springframework.beans.factory.BeanFactoryAware;
5656
import org.springframework.beans.factory.BeanFactoryUtils;
5757
import org.springframework.beans.factory.FactoryBean;
58+
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
5859
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
5960
import org.springframework.util.ClassUtils;
6061
import org.springframework.util.ObjectUtils;
@@ -215,7 +216,7 @@ private PointcutParser initializePointcutParser(ClassLoader cl) {
215216
PointcutParser parser = PointcutParser
216217
.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(
217218
SUPPORTED_PRIMITIVES, cl);
218-
parser.registerPointcutDesignatorHandler(new BeanNamePointcutDesignatorHandler());
219+
parser.registerPointcutDesignatorHandler(new BeanPointcutDesignatorHandler());
219220
return parser;
220221
}
221222

@@ -521,7 +522,7 @@ public String toString() {
521522
* automatically by examining a thread local variable and therefore a matching
522523
* context need not be set on the pointcut.
523524
*/
524-
private class BeanNamePointcutDesignatorHandler implements PointcutDesignatorHandler {
525+
private class BeanPointcutDesignatorHandler implements PointcutDesignatorHandler {
525526

526527
private static final String BEAN_DESIGNATOR_NAME = "bean";
527528

@@ -532,7 +533,7 @@ public String getDesignatorName() {
532533

533534
@Override
534535
public ContextBasedMatcher parse(String expression) {
535-
return new BeanNameContextMatcher(expression);
536+
return new BeanContextMatcher(expression);
536537
}
537538
}
538539

@@ -544,11 +545,11 @@ public ContextBasedMatcher parse(String expression) {
544545
* For static match tests, this matcher abstains to allow the overall
545546
* pointcut to match even when negation is used with the bean() pointcut.
546547
*/
547-
private class BeanNameContextMatcher implements ContextBasedMatcher {
548+
private class BeanContextMatcher implements ContextBasedMatcher {
548549

549550
private final NamePattern expressionPattern;
550551

551-
public BeanNameContextMatcher(String expression) {
552+
public BeanContextMatcher(String expression) {
552553
this.expressionPattern = new NamePattern(expression);
553554
}
554555

@@ -593,27 +594,16 @@ private FuzzyBoolean contextMatch(Class<?> targetType) {
593594
if (targetType != null) {
594595
boolean isFactory = FactoryBean.class.isAssignableFrom(targetType);
595596
return FuzzyBoolean.fromBoolean(
596-
matchesBeanName(isFactory ? BeanFactory.FACTORY_BEAN_PREFIX + advisedBeanName : advisedBeanName));
597+
matchesBean(isFactory ? BeanFactory.FACTORY_BEAN_PREFIX + advisedBeanName : advisedBeanName));
597598
}
598599
else {
599-
return FuzzyBoolean.fromBoolean(matchesBeanName(advisedBeanName) ||
600-
matchesBeanName(BeanFactory.FACTORY_BEAN_PREFIX + advisedBeanName));
600+
return FuzzyBoolean.fromBoolean(matchesBean(advisedBeanName) ||
601+
matchesBean(BeanFactory.FACTORY_BEAN_PREFIX + advisedBeanName));
601602
}
602603
}
603604

604-
private boolean matchesBeanName(String advisedBeanName) {
605-
if (this.expressionPattern.matches(advisedBeanName)) {
606-
return true;
607-
}
608-
if (beanFactory != null) {
609-
String[] aliases = beanFactory.getAliases(advisedBeanName);
610-
for (String alias : aliases) {
611-
if (this.expressionPattern.matches(alias)) {
612-
return true;
613-
}
614-
}
615-
}
616-
return false;
605+
private boolean matchesBean(String advisedBeanName) {
606+
return BeanFactoryAnnotationUtils.isQualifierMatch(this.expressionPattern::matches, advisedBeanName, beanFactory);
617607
}
618608
}
619609

spring-beans/src/main/java/org/springframework/beans/factory/annotation/BeanFactoryAnnotationUtils.java

Lines changed: 53 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 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.
@@ -17,28 +17,32 @@
1717
package org.springframework.beans.factory.annotation;
1818

1919
import java.lang.reflect.Method;
20+
import java.util.function.Predicate;
2021

22+
import org.springframework.beans.BeansException;
2123
import org.springframework.beans.factory.BeanFactory;
2224
import org.springframework.beans.factory.BeanFactoryUtils;
2325
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
26+
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
2427
import org.springframework.beans.factory.config.BeanDefinition;
28+
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
2529
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
2630
import org.springframework.beans.factory.support.AbstractBeanDefinition;
2731
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
2832
import org.springframework.beans.factory.support.RootBeanDefinition;
2933
import org.springframework.core.annotation.AnnotationUtils;
30-
import org.springframework.util.ObjectUtils;
34+
import org.springframework.util.Assert;
3135

3236
/**
3337
* Convenience methods performing bean lookups related to annotations, for example
3438
* Spring's {@link Qualifier @Qualifier} annotation.
3539
*
36-
* @author Chris Beams
3740
* @author Juergen Hoeller
41+
* @author Chris Beams
3842
* @since 3.1.2
3943
* @see BeanFactoryUtils
4044
*/
41-
public class BeanFactoryAnnotationUtils {
45+
public abstract class BeanFactoryAnnotationUtils {
4246

4347
/**
4448
* Obtain a bean of type {@code T} from the given {@code BeanFactory} declaring a
@@ -48,9 +52,16 @@ public class BeanFactoryAnnotationUtils {
4852
* @param beanType the type of bean to retrieve
4953
* @param qualifier the qualifier for selecting between multiple bean matches
5054
* @return the matching bean of type {@code T} (never {@code null})
55+
* @throws NoUniqueBeanDefinitionException if multiple matching beans of type {@code T} found
5156
* @throws NoSuchBeanDefinitionException if no matching bean of type {@code T} found
57+
* @throws BeansException if the bean could not be created
58+
* @see BeanFactory#getBean(Class)
5259
*/
53-
public static <T> T qualifiedBeanOfType(BeanFactory beanFactory, Class<T> beanType, String qualifier) {
60+
public static <T> T qualifiedBeanOfType(BeanFactory beanFactory, Class<T> beanType, String qualifier)
61+
throws BeansException {
62+
63+
Assert.notNull(beanFactory, "BeanFactory must not be null");
64+
5465
if (beanFactory instanceof ConfigurableListableBeanFactory) {
5566
// Full qualifier matching supported.
5667
return qualifiedBeanOfType((ConfigurableListableBeanFactory) beanFactory, beanType, qualifier);
@@ -74,16 +85,14 @@ else if (beanFactory.containsBean(qualifier)) {
7485
* @param beanType the type of bean to retrieve
7586
* @param qualifier the qualifier for selecting between multiple bean matches
7687
* @return the matching bean of type {@code T} (never {@code null})
77-
* @throws NoSuchBeanDefinitionException if no matching bean of type {@code T} found
7888
*/
7989
private static <T> T qualifiedBeanOfType(ConfigurableListableBeanFactory bf, Class<T> beanType, String qualifier) {
8090
String[] candidateBeans = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(bf, beanType);
8191
String matchingBean = null;
8292
for (String beanName : candidateBeans) {
83-
if (isQualifierMatch(qualifier, beanName, bf)) {
93+
if (isQualifierMatch(qualifier::equals, beanName, bf)) {
8494
if (matchingBean != null) {
85-
throw new NoSuchBeanDefinitionException(qualifier, "No unique " + beanType.getSimpleName() +
86-
" bean found for qualifier '" + qualifier + "'");
95+
throw new NoUniqueBeanDefinitionException(beanType, matchingBean, beanName);
8796
}
8897
matchingBean = beanName;
8998
}
@@ -105,40 +114,54 @@ else if (bf.containsBean(qualifier)) {
105114
* Check whether the named bean declares a qualifier of the given name.
106115
* @param qualifier the qualifier to match
107116
* @param beanName the name of the candidate bean
108-
* @param bf the {@code BeanFactory} from which to retrieve the named bean
117+
* @param beanFactory the {@code BeanFactory} from which to retrieve the named bean
109118
* @return {@code true} if either the bean definition (in the XML case)
110119
* or the bean's factory method (in the {@code @Bean} case) defines a matching
111120
* qualifier value (through {@code <qualifier>} or {@code @Qualifier})
121+
* @since 5.0
112122
*/
113-
private static boolean isQualifierMatch(String qualifier, String beanName, ConfigurableListableBeanFactory bf) {
114-
if (bf.containsBean(beanName)) {
123+
public static boolean isQualifierMatch(Predicate<String> qualifier, String beanName, BeanFactory beanFactory) {
124+
// Try quick bean name or alias match first...
125+
if (qualifier.test(beanName)) {
126+
return true;
127+
}
128+
if (beanFactory != null) {
129+
for (String alias : beanFactory.getAliases(beanName)) {
130+
if (qualifier.test(alias)) {
131+
return true;
132+
}
133+
}
115134
try {
116-
BeanDefinition bd = bf.getMergedBeanDefinition(beanName);
117-
// Explicit qualifier metadata on bean definition? (typically in XML definition)
118-
if (bd instanceof AbstractBeanDefinition) {
119-
AbstractBeanDefinition abd = (AbstractBeanDefinition) bd;
120-
AutowireCandidateQualifier candidate = abd.getQualifier(Qualifier.class.getName());
121-
if ((candidate != null && qualifier.equals(candidate.getAttribute(AutowireCandidateQualifier.VALUE_KEY))) ||
122-
qualifier.equals(beanName) || ObjectUtils.containsElement(bf.getAliases(beanName), qualifier)) {
123-
return true;
135+
if (beanFactory instanceof ConfigurableBeanFactory) {
136+
BeanDefinition bd = ((ConfigurableBeanFactory) beanFactory).getMergedBeanDefinition(beanName);
137+
// Explicit qualifier metadata on bean definition? (typically in XML definition)
138+
if (bd instanceof AbstractBeanDefinition) {
139+
AbstractBeanDefinition abd = (AbstractBeanDefinition) bd;
140+
AutowireCandidateQualifier candidate = abd.getQualifier(Qualifier.class.getName());
141+
if (candidate != null) {
142+
Object value = candidate.getAttribute(AutowireCandidateQualifier.VALUE_KEY);
143+
if (value != null && qualifier.test(value.toString())) {
144+
return true;
145+
}
146+
}
124147
}
125-
}
126-
// Corresponding qualifier on factory method? (typically in configuration class)
127-
if (bd instanceof RootBeanDefinition) {
128-
Method factoryMethod = ((RootBeanDefinition) bd).getResolvedFactoryMethod();
129-
if (factoryMethod != null) {
130-
Qualifier targetAnnotation = AnnotationUtils.getAnnotation(factoryMethod, Qualifier.class);
131-
if (targetAnnotation != null) {
132-
return qualifier.equals(targetAnnotation.value());
148+
// Corresponding qualifier on factory method? (typically in configuration class)
149+
if (bd instanceof RootBeanDefinition) {
150+
Method factoryMethod = ((RootBeanDefinition) bd).getResolvedFactoryMethod();
151+
if (factoryMethod != null) {
152+
Qualifier targetAnnotation = AnnotationUtils.getAnnotation(factoryMethod, Qualifier.class);
153+
if (targetAnnotation != null) {
154+
return qualifier.test(targetAnnotation.value());
155+
}
133156
}
134157
}
135158
}
136159
// Corresponding qualifier on bean implementation class? (for custom user types)
137-
Class<?> beanType = bf.getType(beanName);
160+
Class<?> beanType = beanFactory.getType(beanName);
138161
if (beanType != null) {
139162
Qualifier targetAnnotation = AnnotationUtils.getAnnotation(beanType, Qualifier.class);
140163
if (targetAnnotation != null) {
141-
return qualifier.equals(targetAnnotation.value());
164+
return qualifier.test(targetAnnotation.value());
142165
}
143166
}
144167
}

spring-context/src/test/java/org/springframework/aop/aspectj/BeanNamePointcutTests.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2016 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.
@@ -69,6 +69,7 @@ public void setUp() {
6969
counterAspect.reset();
7070
}
7171

72+
7273
// We don't need to test all combination of pointcuts due to BeanNamePointcutMatchingTests
7374

7475
@Test

spring-context/src/test/resources/org/springframework/aop/aspectj/BeanNamePointcutTests.xml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@
2121
</aop:aspect>
2222
</aop:config>
2323

24-
<bean id="testBean1" name="myBean" class="org.springframework.tests.sample.beans.TestBean"/>
24+
<bean id="tb1" name="testBean1" class="org.springframework.tests.sample.beans.TestBean">
25+
<qualifier value="myBean"/>
26+
</bean>
2527

26-
<bean id="testBean2" class="org.springframework.tests.sample.beans.TestBean"/>
28+
<bean id="tb2" name="testBean2" class="org.springframework.tests.sample.beans.TestBean"/>
2729

2830
<bean id="testBeanContainingNestedBean" class="org.springframework.tests.sample.beans.TestBean">
2931
<property name="doctor">
@@ -50,7 +52,7 @@
5052
<bean id="counterAspect" class="org.springframework.aop.aspectj.Counter"/>
5153

5254
<aop:config>
53-
<aop:advisor pointcut="bean(*This) and !bean(dont*)" advice-ref="testInterceptor"/>
55+
<aop:advisor pointcut="bean(*This) and !bean(dont*)" advice-ref="testInterceptor"/>
5456
</aop:config>
5557

5658
<bean id="interceptThis" class="org.springframework.tests.sample.beans.TestBean"/>

0 commit comments

Comments
 (0)