Skip to content

Commit d5efe4f

Browse files
committed
Detect event listener methods behind interface proxies as well
Issue: SPR-13650
1 parent bc7bcab commit d5efe4f

File tree

10 files changed

+357
-79
lines changed

10 files changed

+357
-79
lines changed

spring-aop/src/main/java/org/springframework/aop/framework/AbstractAdvisingBeanPostProcessor.java

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 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.
@@ -81,13 +81,12 @@ public Object postProcessAfterInitialization(Object bean, String beanName) {
8181
}
8282

8383
if (isEligible(bean, beanName)) {
84-
ProxyFactory proxyFactory = new ProxyFactory();
85-
proxyFactory.copyFrom(this);
86-
proxyFactory.setTarget(bean);
84+
ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
8785
if (!proxyFactory.isProxyTargetClass()) {
8886
evaluateProxyInterfaces(bean.getClass(), proxyFactory);
8987
}
9088
proxyFactory.addAdvisor(this.advisor);
89+
customizeProxyFactory(proxyFactory);
9190
return proxyFactory.getProxy(getProxyClassLoader());
9291
}
9392

@@ -131,4 +130,38 @@ protected boolean isEligible(Class<?> targetClass) {
131130
return eligible;
132131
}
133132

133+
/**
134+
* Prepare a {@link ProxyFactory} for the given bean.
135+
* <p>Subclasses may customize the handling of the target instance and in
136+
* particular the exposure of the target class. The default introspection
137+
* of interfaces for non-target-class proxies and the configured advisor
138+
* will be applied afterwards; {@link #customizeProxyFactory} allows for
139+
* late customizations of those parts right before proxy creation.
140+
* @param bean the bean instance to create a proxy for
141+
* @param beanName the corresponding bean name
142+
* @return the ProxyFactory, initialized with this processor's
143+
* {@link ProxyConfig} settings and the specified bean
144+
* @since 4.2.3
145+
* @see #customizeProxyFactory
146+
*/
147+
protected ProxyFactory prepareProxyFactory(Object bean, String beanName) {
148+
ProxyFactory proxyFactory = new ProxyFactory();
149+
proxyFactory.copyFrom(this);
150+
proxyFactory.setTarget(bean);
151+
return proxyFactory;
152+
}
153+
154+
/**
155+
* Subclasses may choose to implement this: for example,
156+
* to change the interfaces exposed.
157+
* <p>The default implementation is empty.
158+
* @param proxyFactory ProxyFactory that is already configured with
159+
* target, advisor and interfaces and will be used to create the proxy
160+
* immediately after this method returns
161+
* @since 4.2.3
162+
* @see #prepareProxyFactory
163+
*/
164+
protected void customizeProxyFactory(ProxyFactory proxyFactory) {
165+
}
166+
134167
}

spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AbstractAutoProxyCreator.java

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 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.
@@ -330,7 +330,8 @@ protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey)
330330
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
331331
if (specificInterceptors != DO_NOT_PROXY) {
332332
this.advisedBeans.put(cacheKey, Boolean.TRUE);
333-
Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
333+
Object proxy = createProxy(
334+
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
334335
this.proxyTypes.put(cacheKey, proxy.getClass());
335336
return proxy;
336337
}
@@ -419,6 +420,10 @@ protected TargetSource getCustomTargetSource(Class<?> beanClass, String beanName
419420
protected Object createProxy(
420421
Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
421422

423+
if (beanName != null && this.beanFactory instanceof ConfigurableListableBeanFactory) {
424+
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
425+
}
426+
422427
ProxyFactory proxyFactory = new ProxyFactory();
423428
proxyFactory.copyFrom(this);
424429

@@ -490,7 +495,7 @@ protected Advisor[] buildAdvisors(String beanName, Object[] specificInterceptors
490495
List<Object> allInterceptors = new ArrayList<Object>();
491496
if (specificInterceptors != null) {
492497
allInterceptors.addAll(Arrays.asList(specificInterceptors));
493-
if (commonInterceptors != null) {
498+
if (commonInterceptors.length > 0) {
494499
if (this.applyCommonInterceptorsFirst) {
495500
allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
496501
}
@@ -500,7 +505,7 @@ protected Advisor[] buildAdvisors(String beanName, Object[] specificInterceptors
500505
}
501506
}
502507
if (logger.isDebugEnabled()) {
503-
int nrOfCommonInterceptors = (commonInterceptors != null ? commonInterceptors.length : 0);
508+
int nrOfCommonInterceptors = commonInterceptors.length;
504509
int nrOfSpecificInterceptors = (specificInterceptors != null ? specificInterceptors.length : 0);
505510
logger.debug("Creating implicit proxy for bean '" + beanName + "' with " + nrOfCommonInterceptors +
506511
" common interceptors and " + nrOfSpecificInterceptors + " specific interceptors");
@@ -518,8 +523,8 @@ protected Advisor[] buildAdvisors(String beanName, Object[] specificInterceptors
518523
* @see #setInterceptorNames
519524
*/
520525
private Advisor[] resolveInterceptorNames() {
521-
ConfigurableBeanFactory cbf = (this.beanFactory instanceof ConfigurableBeanFactory) ?
522-
(ConfigurableBeanFactory) this.beanFactory : null;
526+
ConfigurableBeanFactory cbf = (this.beanFactory instanceof ConfigurableBeanFactory ?
527+
(ConfigurableBeanFactory) this.beanFactory : null);
523528
List<Advisor> advisors = new ArrayList<Advisor>();
524529
for (String beanName : this.interceptorNames) {
525530
if (cbf == null || !cbf.isCurrentlyInCreation(beanName)) {
@@ -536,7 +541,7 @@ private Advisor[] resolveInterceptorNames() {
536541
* <p>The default implementation is empty.
537542
* @param proxyFactory ProxyFactory that is already configured with
538543
* TargetSource and interfaces and will be used to create the proxy
539-
* immediably after this method returns
544+
* immediately after this method returns
540545
*/
541546
protected void customizeProxyFactory(ProxyFactory proxyFactory) {
542547
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2002-2015 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.aop.framework.autoproxy;
18+
19+
import org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor;
20+
import org.springframework.aop.framework.ProxyFactory;
21+
import org.springframework.beans.factory.BeanFactory;
22+
import org.springframework.beans.factory.BeanFactoryAware;
23+
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
24+
25+
/**
26+
* Extension of {@link AbstractAutoProxyCreator} which implements {@link BeanFactoryAware},
27+
* adds exposure of the original target class for each proxied bean
28+
* ({@link AutoProxyUtils#ORIGINAL_TARGET_CLASS_ATTRIBUTE}),
29+
* and participates in an externally enforced target-class mode for any given bean
30+
* ({@link AutoProxyUtils#PRESERVE_TARGET_CLASS_ATTRIBUTE}).
31+
*
32+
* @author Juergen Hoeller
33+
* @since 4.2.3
34+
* @see AutoProxyUtils#shouldProxyTargetClass
35+
* @see AutoProxyUtils#determineTargetClass
36+
*/
37+
@SuppressWarnings("serial")
38+
public abstract class AbstractBeanFactoryAwareAdvisingPostProcessor extends AbstractAdvisingBeanPostProcessor
39+
implements BeanFactoryAware {
40+
41+
private ConfigurableListableBeanFactory beanFactory;
42+
43+
44+
@Override
45+
public void setBeanFactory(BeanFactory beanFactory) {
46+
this.beanFactory = (beanFactory instanceof ConfigurableListableBeanFactory ?
47+
(ConfigurableListableBeanFactory) beanFactory : null);
48+
}
49+
50+
@Override
51+
protected ProxyFactory prepareProxyFactory(Object bean, String beanName) {
52+
if (this.beanFactory != null) {
53+
AutoProxyUtils.exposeTargetClass(this.beanFactory, beanName, bean.getClass());
54+
}
55+
56+
ProxyFactory proxyFactory = super.prepareProxyFactory(bean, beanName);
57+
if (!proxyFactory.isProxyTargetClass() && this.beanFactory != null &&
58+
AutoProxyUtils.shouldProxyTargetClass(this.beanFactory, beanName)) {
59+
proxyFactory.setProxyTargetClass(true);
60+
}
61+
return proxyFactory;
62+
}
63+
64+
}

spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AutoProxyUtils.java

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2015 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.
@@ -37,10 +37,21 @@ public abstract class AutoProxyUtils {
3737
* <p>Proxy factories can set this attribute if they built a target class proxy
3838
* for a specific bean, and want to enforce that that bean can always be cast
3939
* to its target class (even if AOP advices get applied through auto-proxying).
40+
* @see #shouldProxyTargetClass
4041
*/
4142
public static final String PRESERVE_TARGET_CLASS_ATTRIBUTE =
4243
Conventions.getQualifiedAttributeName(AutoProxyUtils.class, "preserveTargetClass");
4344

45+
/**
46+
* Bean definition attribute that indicates the original target class of an
47+
* auto-proxied bean, e.g. to be used for the introspection of annotations
48+
* on the target class behind an interface-based proxy.
49+
* @since 4.2.3
50+
* @see #determineTargetClass
51+
*/
52+
public static final String ORIGINAL_TARGET_CLASS_ATTRIBUTE =
53+
Conventions.getQualifiedAttributeName(AutoProxyUtils.class, "originalTargetClass");
54+
4455

4556
/**
4657
* Determine whether the given bean should be proxied with its target
@@ -59,4 +70,40 @@ public static boolean shouldProxyTargetClass(ConfigurableListableBeanFactory bea
5970
return false;
6071
}
6172

73+
/**
74+
* Determine the original target class for the specified bean, if possible,
75+
* otherwise falling back to a regular {@code getType} lookup.
76+
* @param beanFactory the containing ConfigurableListableBeanFactory
77+
* @param beanName the name of the bean
78+
* @return the original target class as stored in the bean definition, if any
79+
* @since 4.2.3
80+
* @see org.springframework.beans.factory.BeanFactory#getType(String)
81+
*/
82+
public static Class<?> determineTargetClass(ConfigurableListableBeanFactory beanFactory, String beanName) {
83+
if (beanName == null) {
84+
return null;
85+
}
86+
if (beanFactory.containsBeanDefinition(beanName)) {
87+
BeanDefinition bd = beanFactory.getMergedBeanDefinition(beanName);
88+
Class<?> targetClass = (Class<?>) bd.getAttribute(ORIGINAL_TARGET_CLASS_ATTRIBUTE);
89+
if (targetClass != null) {
90+
return targetClass;
91+
}
92+
}
93+
return beanFactory.getType(beanName);
94+
}
95+
96+
/**
97+
* Expose the given target class for the specified bean.
98+
* @param beanFactory the containing ConfigurableListableBeanFactory
99+
* @param beanName the name of the bean
100+
* @param targetClass the corresponding target class
101+
* @since 4.2.3
102+
*/
103+
static void exposeTargetClass(ConfigurableListableBeanFactory beanFactory, String beanName, Class<?> targetClass) {
104+
if (beanFactory.containsBeanDefinition(beanName)) {
105+
beanFactory.getMergedBeanDefinition(beanName).setAttribute(ORIGINAL_TARGET_CLASS_ATTRIBUTE, targetClass);
106+
}
107+
}
108+
62109
}

0 commit comments

Comments
 (0)