Detect event listener methods behind interface proxies as well

Issue: SPR-13650
This commit is contained in:
Juergen Hoeller
2015-11-09 15:00:03 +01:00
parent bc7bcab578
commit d5efe4f983
10 changed files with 361 additions and 83 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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.
@@ -81,13 +81,12 @@ public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSu
}
if (isEligible(bean, beanName)) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
proxyFactory.setTarget(bean);
ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
if (!proxyFactory.isProxyTargetClass()) {
evaluateProxyInterfaces(bean.getClass(), proxyFactory);
}
proxyFactory.addAdvisor(this.advisor);
customizeProxyFactory(proxyFactory);
return proxyFactory.getProxy(getProxyClassLoader());
}
@@ -131,4 +130,38 @@ public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSu
return eligible;
}
/**
* Prepare a {@link ProxyFactory} for the given bean.
* <p>Subclasses may customize the handling of the target instance and in
* particular the exposure of the target class. The default introspection
* of interfaces for non-target-class proxies and the configured advisor
* will be applied afterwards; {@link #customizeProxyFactory} allows for
* late customizations of those parts right before proxy creation.
* @param bean the bean instance to create a proxy for
* @param beanName the corresponding bean name
* @return the ProxyFactory, initialized with this processor's
* {@link ProxyConfig} settings and the specified bean
* @since 4.2.3
* @see #customizeProxyFactory
*/
protected ProxyFactory prepareProxyFactory(Object bean, String beanName) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
proxyFactory.setTarget(bean);
return proxyFactory;
}
/**
* Subclasses may choose to implement this: for example,
* to change the interfaces exposed.
* <p>The default implementation is empty.
* @param proxyFactory ProxyFactory that is already configured with
* target, advisor and interfaces and will be used to create the proxy
* immediately after this method returns
* @since 4.2.3
* @see #prepareProxyFactory
*/
protected void customizeProxyFactory(ProxyFactory proxyFactory) {
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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.
@@ -330,7 +330,8 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
@@ -419,6 +420,10 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
protected Object createProxy(
Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
if (beanName != null && this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
@@ -490,7 +495,7 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
List<Object> allInterceptors = new ArrayList<Object>();
if (specificInterceptors != null) {
allInterceptors.addAll(Arrays.asList(specificInterceptors));
if (commonInterceptors != null) {
if (commonInterceptors.length > 0) {
if (this.applyCommonInterceptorsFirst) {
allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
}
@@ -500,7 +505,7 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
}
}
if (logger.isDebugEnabled()) {
int nrOfCommonInterceptors = (commonInterceptors != null ? commonInterceptors.length : 0);
int nrOfCommonInterceptors = commonInterceptors.length;
int nrOfSpecificInterceptors = (specificInterceptors != null ? specificInterceptors.length : 0);
logger.debug("Creating implicit proxy for bean '" + beanName + "' with " + nrOfCommonInterceptors +
" common interceptors and " + nrOfSpecificInterceptors + " specific interceptors");
@@ -518,8 +523,8 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
* @see #setInterceptorNames
*/
private Advisor[] resolveInterceptorNames() {
ConfigurableBeanFactory cbf = (this.beanFactory instanceof ConfigurableBeanFactory) ?
(ConfigurableBeanFactory) this.beanFactory : null;
ConfigurableBeanFactory cbf = (this.beanFactory instanceof ConfigurableBeanFactory ?
(ConfigurableBeanFactory) this.beanFactory : null);
List<Advisor> advisors = new ArrayList<Advisor>();
for (String beanName : this.interceptorNames) {
if (cbf == null || !cbf.isCurrentlyInCreation(beanName)) {
@@ -536,7 +541,7 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
* <p>The default implementation is empty.
* @param proxyFactory ProxyFactory that is already configured with
* TargetSource and interfaces and will be used to create the proxy
* immediably after this method returns
* immediately after this method returns
*/
protected void customizeProxyFactory(ProxyFactory proxyFactory) {
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2002-2015 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.aop.framework.autoproxy;
import org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
/**
* Extension of {@link AbstractAutoProxyCreator} which implements {@link BeanFactoryAware},
* adds exposure of the original target class for each proxied bean
* ({@link AutoProxyUtils#ORIGINAL_TARGET_CLASS_ATTRIBUTE}),
* and participates in an externally enforced target-class mode for any given bean
* ({@link AutoProxyUtils#PRESERVE_TARGET_CLASS_ATTRIBUTE}).
*
* @author Juergen Hoeller
* @since 4.2.3
* @see AutoProxyUtils#shouldProxyTargetClass
* @see AutoProxyUtils#determineTargetClass
*/
@SuppressWarnings("serial")
public abstract class AbstractBeanFactoryAwareAdvisingPostProcessor extends AbstractAdvisingBeanPostProcessor
implements BeanFactoryAware {
private ConfigurableListableBeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = (beanFactory instanceof ConfigurableListableBeanFactory ?
(ConfigurableListableBeanFactory) beanFactory : null);
}
@Override
protected ProxyFactory prepareProxyFactory(Object bean, String beanName) {
if (this.beanFactory != null) {
AutoProxyUtils.exposeTargetClass(this.beanFactory, beanName, bean.getClass());
}
ProxyFactory proxyFactory = super.prepareProxyFactory(bean, beanName);
if (!proxyFactory.isProxyTargetClass() && this.beanFactory != null &&
AutoProxyUtils.shouldProxyTargetClass(this.beanFactory, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
return proxyFactory;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2015 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.
@@ -37,10 +37,21 @@ public abstract class AutoProxyUtils {
* <p>Proxy factories can set this attribute if they built a target class proxy
* for a specific bean, and want to enforce that that bean can always be cast
* to its target class (even if AOP advices get applied through auto-proxying).
* @see #shouldProxyTargetClass
*/
public static final String PRESERVE_TARGET_CLASS_ATTRIBUTE =
Conventions.getQualifiedAttributeName(AutoProxyUtils.class, "preserveTargetClass");
/**
* Bean definition attribute that indicates the original target class of an
* auto-proxied bean, e.g. to be used for the introspection of annotations
* on the target class behind an interface-based proxy.
* @since 4.2.3
* @see #determineTargetClass
*/
public static final String ORIGINAL_TARGET_CLASS_ATTRIBUTE =
Conventions.getQualifiedAttributeName(AutoProxyUtils.class, "originalTargetClass");
/**
* Determine whether the given bean should be proxied with its target
@@ -59,4 +70,40 @@ public abstract class AutoProxyUtils {
return false;
}
/**
* Determine the original target class for the specified bean, if possible,
* otherwise falling back to a regular {@code getType} lookup.
* @param beanFactory the containing ConfigurableListableBeanFactory
* @param beanName the name of the bean
* @return the original target class as stored in the bean definition, if any
* @since 4.2.3
* @see org.springframework.beans.factory.BeanFactory#getType(String)
*/
public static Class<?> determineTargetClass(ConfigurableListableBeanFactory beanFactory, String beanName) {
if (beanName == null) {
return null;
}
if (beanFactory.containsBeanDefinition(beanName)) {
BeanDefinition bd = beanFactory.getMergedBeanDefinition(beanName);
Class<?> targetClass = (Class<?>) bd.getAttribute(ORIGINAL_TARGET_CLASS_ATTRIBUTE);
if (targetClass != null) {
return targetClass;
}
}
return beanFactory.getType(beanName);
}
/**
* Expose the given target class for the specified bean.
* @param beanFactory the containing ConfigurableListableBeanFactory
* @param beanName the name of the bean
* @param targetClass the corresponding target class
* @since 4.2.3
*/
static void exposeTargetClass(ConfigurableListableBeanFactory beanFactory, String beanName, Class<?> targetClass) {
if (beanFactory.containsBeanDefinition(beanName)) {
beanFactory.getMergedBeanDefinition(beanName).setAttribute(ORIGINAL_TARGET_CLASS_ATTRIBUTE, targetClass);
}
}
}