提交 d5efe4f9 编写于 作者: J Juergen Hoeller

Detect event listener methods behind interface proxies as well

Issue: SPR-13650
上级 bc7bcab5
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -81,13 +81,12 @@ public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSu ...@@ -81,13 +81,12 @@ public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSu
} }
if (isEligible(bean, beanName)) { if (isEligible(bean, beanName)) {
ProxyFactory proxyFactory = new ProxyFactory(); ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
proxyFactory.copyFrom(this);
proxyFactory.setTarget(bean);
if (!proxyFactory.isProxyTargetClass()) { if (!proxyFactory.isProxyTargetClass()) {
evaluateProxyInterfaces(bean.getClass(), proxyFactory); evaluateProxyInterfaces(bean.getClass(), proxyFactory);
} }
proxyFactory.addAdvisor(this.advisor); proxyFactory.addAdvisor(this.advisor);
customizeProxyFactory(proxyFactory);
return proxyFactory.getProxy(getProxyClassLoader()); return proxyFactory.getProxy(getProxyClassLoader());
} }
...@@ -131,4 +130,38 @@ public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSu ...@@ -131,4 +130,38 @@ public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSu
return eligible; 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) {
}
} }
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -330,7 +330,8 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport ...@@ -330,7 +330,8 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) { if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE); 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()); this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy; return proxy;
} }
...@@ -419,6 +420,10 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport ...@@ -419,6 +420,10 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
protected Object createProxy( protected Object createProxy(
Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) { 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 proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this); proxyFactory.copyFrom(this);
...@@ -490,7 +495,7 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport ...@@ -490,7 +495,7 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
List<Object> allInterceptors = new ArrayList<Object>(); List<Object> allInterceptors = new ArrayList<Object>();
if (specificInterceptors != null) { if (specificInterceptors != null) {
allInterceptors.addAll(Arrays.asList(specificInterceptors)); allInterceptors.addAll(Arrays.asList(specificInterceptors));
if (commonInterceptors != null) { if (commonInterceptors.length > 0) {
if (this.applyCommonInterceptorsFirst) { if (this.applyCommonInterceptorsFirst) {
allInterceptors.addAll(0, Arrays.asList(commonInterceptors)); allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
} }
...@@ -500,7 +505,7 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport ...@@ -500,7 +505,7 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
} }
} }
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
int nrOfCommonInterceptors = (commonInterceptors != null ? commonInterceptors.length : 0); int nrOfCommonInterceptors = commonInterceptors.length;
int nrOfSpecificInterceptors = (specificInterceptors != null ? specificInterceptors.length : 0); int nrOfSpecificInterceptors = (specificInterceptors != null ? specificInterceptors.length : 0);
logger.debug("Creating implicit proxy for bean '" + beanName + "' with " + nrOfCommonInterceptors + logger.debug("Creating implicit proxy for bean '" + beanName + "' with " + nrOfCommonInterceptors +
" common interceptors and " + nrOfSpecificInterceptors + " specific interceptors"); " common interceptors and " + nrOfSpecificInterceptors + " specific interceptors");
...@@ -518,8 +523,8 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport ...@@ -518,8 +523,8 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
* @see #setInterceptorNames * @see #setInterceptorNames
*/ */
private Advisor[] resolveInterceptorNames() { private Advisor[] resolveInterceptorNames() {
ConfigurableBeanFactory cbf = (this.beanFactory instanceof ConfigurableBeanFactory) ? ConfigurableBeanFactory cbf = (this.beanFactory instanceof ConfigurableBeanFactory ?
(ConfigurableBeanFactory) this.beanFactory : null; (ConfigurableBeanFactory) this.beanFactory : null);
List<Advisor> advisors = new ArrayList<Advisor>(); List<Advisor> advisors = new ArrayList<Advisor>();
for (String beanName : this.interceptorNames) { for (String beanName : this.interceptorNames) {
if (cbf == null || !cbf.isCurrentlyInCreation(beanName)) { if (cbf == null || !cbf.isCurrentlyInCreation(beanName)) {
...@@ -536,7 +541,7 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport ...@@ -536,7 +541,7 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
* <p>The default implementation is empty. * <p>The default implementation is empty.
* @param proxyFactory ProxyFactory that is already configured with * @param proxyFactory ProxyFactory that is already configured with
* TargetSource and interfaces and will be used to create the proxy * 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) { protected void customizeProxyFactory(ProxyFactory proxyFactory) {
} }
......
/*
* 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;
}
}
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -37,10 +37,21 @@ public abstract class AutoProxyUtils { ...@@ -37,10 +37,21 @@ public abstract class AutoProxyUtils {
* <p>Proxy factories can set this attribute if they built a target class proxy * <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 * 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). * to its target class (even if AOP advices get applied through auto-proxying).
* @see #shouldProxyTargetClass
*/ */
public static final String PRESERVE_TARGET_CLASS_ATTRIBUTE = public static final String PRESERVE_TARGET_CLASS_ATTRIBUTE =
Conventions.getQualifiedAttributeName(AutoProxyUtils.class, "preserveTargetClass"); 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 * Determine whether the given bean should be proxied with its target
...@@ -59,4 +70,40 @@ public abstract class AutoProxyUtils { ...@@ -59,4 +70,40 @@ public abstract class AutoProxyUtils {
return false; 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);
}
}
} }
...@@ -19,7 +19,6 @@ package org.springframework.context.event; ...@@ -19,7 +19,6 @@ package org.springframework.context.event;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
...@@ -28,6 +27,8 @@ import java.util.concurrent.ConcurrentHashMap; ...@@ -28,6 +27,8 @@ import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.aop.framework.autoproxy.AutoProxyUtils;
import org.springframework.aop.scope.ScopedObject;
import org.springframework.aop.scope.ScopedProxyUtils; import org.springframework.aop.scope.ScopedProxyUtils;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanInitializationException; import org.springframework.beans.factory.BeanInitializationException;
...@@ -36,10 +37,10 @@ import org.springframework.context.ApplicationContext; ...@@ -36,10 +37,10 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
/** /**
* Register {@link EventListener} annotated method as individual {@link ApplicationListener} * Register {@link EventListener} annotated method as individual {@link ApplicationListener}
...@@ -66,7 +67,6 @@ public class EventListenerMethodProcessor implements SmartInitializingSingleton, ...@@ -66,7 +67,6 @@ public class EventListenerMethodProcessor implements SmartInitializingSingleton,
Assert.isTrue(applicationContext instanceof ConfigurableApplicationContext, Assert.isTrue(applicationContext instanceof ConfigurableApplicationContext,
"ApplicationContext does not implement ConfigurableApplicationContext"); "ApplicationContext does not implement ConfigurableApplicationContext");
this.applicationContext = (ConfigurableApplicationContext) applicationContext; this.applicationContext = (ConfigurableApplicationContext) applicationContext;
} }
@Override @Override
...@@ -75,21 +75,36 @@ public class EventListenerMethodProcessor implements SmartInitializingSingleton, ...@@ -75,21 +75,36 @@ public class EventListenerMethodProcessor implements SmartInitializingSingleton,
String[] allBeanNames = this.applicationContext.getBeanNamesForType(Object.class); String[] allBeanNames = this.applicationContext.getBeanNamesForType(Object.class);
for (String beanName : allBeanNames) { for (String beanName : allBeanNames) {
if (!ScopedProxyUtils.isScopedTarget(beanName)) { if (!ScopedProxyUtils.isScopedTarget(beanName)) {
Class<?> type = this.applicationContext.getType(beanName); Class<?> type = AutoProxyUtils.determineTargetClass(this.applicationContext.getBeanFactory(), beanName);
try { if (type != null) {
processBean(factories, beanName, type); if (ScopedObject.class.isAssignableFrom(type)) {
} try {
catch (Throwable ex) { type = AutoProxyUtils.determineTargetClass(this.applicationContext.getBeanFactory(),
throw new BeanInitializationException("Failed to process @EventListener " + ScopedProxyUtils.getTargetBeanName(beanName));
"annotation on bean with name '" + beanName + "'", ex); }
catch (Throwable ex) {
// An invalid scoped proxy arrangement - let's ignore it.
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve target bean for scoped proxy '" + beanName + "'", ex);
}
}
}
try {
processBean(factories, beanName, type);
}
catch (Throwable ex) {
throw new BeanInitializationException("Failed to process @EventListener " +
"annotation on bean with name '" + beanName + "'", ex);
}
} }
} }
} }
} }
/** /**
* Return the {@link EventListenerFactory} instances to use to handle {@link EventListener} * Return the {@link EventListenerFactory} instances to use to handle
* annotated methods. * {@link EventListener} annotated methods.
*/ */
protected List<EventListenerFactory> getEventListenerFactories() { protected List<EventListenerFactory> getEventListenerFactories() {
Map<String, EventListenerFactory> beans = Map<String, EventListenerFactory> beans =
...@@ -99,29 +114,15 @@ public class EventListenerMethodProcessor implements SmartInitializingSingleton, ...@@ -99,29 +114,15 @@ public class EventListenerMethodProcessor implements SmartInitializingSingleton,
return allFactories; return allFactories;
} }
protected void processBean(List<EventListenerFactory> factories, String beanName, final Class<?> targetType) { protected void processBean(final List<EventListenerFactory> factories, final String beanName, final Class<?> targetType) {
if (!this.nonAnnotatedClasses.contains(targetType)) { if (!this.nonAnnotatedClasses.contains(targetType)) {
final Set<Method> annotatedMethods = new LinkedHashSet<Method>(1); Map<Method, EventListener> annotatedMethods = MethodIntrospector.selectMethods(targetType,
Method[] methods = ReflectionUtils.getUniqueDeclaredMethods(targetType); new MethodIntrospector.MetadataLookup<EventListener>() {
for (Method method : methods) { @Override
EventListener eventListener = AnnotationUtils.findAnnotation(method, EventListener.class); public EventListener inspect(Method method) {
if (eventListener == null) { return AnnotationUtils.findAnnotation(method, EventListener.class);
continue;
}
for (EventListenerFactory factory : factories) {
if (factory.supportsMethod(method)) {
ApplicationListener<?> applicationListener =
factory.createApplicationListener(beanName, targetType, method);
if (applicationListener instanceof ApplicationListenerMethodAdapter) {
((ApplicationListenerMethodAdapter) applicationListener)
.init(this.applicationContext, this.evaluator);
} }
this.applicationContext.addApplicationListener(applicationListener); });
annotatedMethods.add(method);
break;
}
}
}
if (annotatedMethods.isEmpty()) { if (annotatedMethods.isEmpty()) {
this.nonAnnotatedClasses.add(targetType); this.nonAnnotatedClasses.add(targetType);
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
...@@ -130,6 +131,22 @@ public class EventListenerMethodProcessor implements SmartInitializingSingleton, ...@@ -130,6 +131,22 @@ public class EventListenerMethodProcessor implements SmartInitializingSingleton,
} }
else { else {
// Non-empty set of methods // Non-empty set of methods
for (Method method : annotatedMethods.keySet()) {
for (EventListenerFactory factory : factories) {
if (factory.supportsMethod(method)) {
Method methodToUse = MethodIntrospector.selectInvocableMethod(
method, this.applicationContext.getType(beanName));
ApplicationListener<?> applicationListener =
factory.createApplicationListener(beanName, targetType, methodToUse);
if (applicationListener instanceof ApplicationListenerMethodAdapter) {
((ApplicationListenerMethodAdapter) applicationListener)
.init(this.applicationContext, this.evaluator);
}
this.applicationContext.addApplicationListener(applicationListener);
break;
}
}
}
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" + logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +
beanName + "': " + annotatedMethods); beanName + "': " + annotatedMethods);
......
...@@ -22,10 +22,9 @@ import java.util.concurrent.Executor; ...@@ -22,10 +22,9 @@ import java.util.concurrent.Executor;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor; import org.springframework.aop.framework.autoproxy.AbstractBeanFactoryAwareAdvisingPostProcessor;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.core.task.TaskExecutor; import org.springframework.core.task.TaskExecutor;
...@@ -62,7 +61,7 @@ import org.springframework.util.Assert; ...@@ -62,7 +61,7 @@ import org.springframework.util.Assert;
* @see ScheduledAnnotationBeanPostProcessor * @see ScheduledAnnotationBeanPostProcessor
*/ */
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class AsyncAnnotationBeanPostProcessor extends AbstractAdvisingBeanPostProcessor implements BeanFactoryAware { public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor {
/** /**
* The default name of the {@link TaskExecutor} bean to pick up: "taskExecutor". * The default name of the {@link TaskExecutor} bean to pick up: "taskExecutor".
...@@ -119,6 +118,8 @@ public class AsyncAnnotationBeanPostProcessor extends AbstractAdvisingBeanPostPr ...@@ -119,6 +118,8 @@ public class AsyncAnnotationBeanPostProcessor extends AbstractAdvisingBeanPostPr
@Override @Override
public void setBeanFactory(BeanFactory beanFactory) { public void setBeanFactory(BeanFactory beanFactory) {
super.setBeanFactory(beanFactory);
Executor executorToUse = this.executor; Executor executorToUse = this.executor;
if (executorToUse == null) { if (executorToUse == null) {
try { try {
......
...@@ -19,7 +19,6 @@ package org.springframework.scheduling.annotation; ...@@ -19,7 +19,6 @@ package org.springframework.scheduling.annotation;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TimeZone; import java.util.TimeZone;
...@@ -43,6 +42,7 @@ import org.springframework.context.ApplicationContextAware; ...@@ -43,6 +42,7 @@ import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.springframework.context.EmbeddedValueResolverAware; import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.TaskScheduler;
...@@ -54,7 +54,6 @@ import org.springframework.scheduling.support.CronTrigger; ...@@ -54,7 +54,6 @@ import org.springframework.scheduling.support.CronTrigger;
import org.springframework.scheduling.support.ScheduledMethodRunnable; import org.springframework.scheduling.support.ScheduledMethodRunnable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.MethodCallback;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.util.StringValueResolver; import org.springframework.util.StringValueResolver;
...@@ -236,17 +235,15 @@ public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor, ...@@ -236,17 +235,15 @@ public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor,
public Object postProcessAfterInitialization(final Object bean, String beanName) { public Object postProcessAfterInitialization(final Object bean, String beanName) {
Class<?> targetClass = AopUtils.getTargetClass(bean); Class<?> targetClass = AopUtils.getTargetClass(bean);
if (!this.nonAnnotatedClasses.contains(targetClass)) { if (!this.nonAnnotatedClasses.contains(targetClass)) {
final Set<Method> annotatedMethods = new LinkedHashSet<Method>(1); Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass,
ReflectionUtils.doWithMethods(targetClass, new MethodCallback() { new MethodIntrospector.MetadataLookup<Set<Scheduled>>() {
@Override @Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { public Set<Scheduled> inspect(Method method) {
for (Scheduled scheduled : Set<Scheduled> scheduledMethods =
AnnotationUtils.getRepeatableAnnotations(method, Scheduled.class, Schedules.class)) { AnnotationUtils.getRepeatableAnnotations(method, Scheduled.class, Schedules.class);
processScheduled(scheduled, method, bean); return (!scheduledMethods.isEmpty() ? scheduledMethods : null);
annotatedMethods.add(method); }
} });
}
});
if (annotatedMethods.isEmpty()) { if (annotatedMethods.isEmpty()) {
this.nonAnnotatedClasses.add(targetClass); this.nonAnnotatedClasses.add(targetClass);
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
...@@ -255,6 +252,12 @@ public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor, ...@@ -255,6 +252,12 @@ public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor,
} }
else { else {
// Non-empty set of methods // Non-empty set of methods
for (Map.Entry<Method, Set<Scheduled>> entry : annotatedMethods.entrySet()) {
Method method = entry.getKey();
for (Scheduled scheduled : entry.getValue()) {
processScheduled(scheduled, method, bean);
}
}
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug(annotatedMethods.size() + " @Scheduled methods processed on bean '" + beanName + logger.debug(annotatedMethods.size() + " @Scheduled methods processed on bean '" + beanName +
"': " + annotatedMethods); "': " + annotatedMethods);
...@@ -285,7 +288,7 @@ public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor, ...@@ -285,7 +288,7 @@ public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor,
"@Scheduled method '%s' found on bean target class '%s' but not " + "@Scheduled method '%s' found on bean target class '%s' but not " +
"found in any interface(s) for a dynamic proxy. Either pull the " + "found in any interface(s) for a dynamic proxy. Either pull the " +
"method up to a declared interface or switch to subclass (CGLIB) " + "method up to a declared interface or switch to subclass (CGLIB) " +
"proxies by setting proxy-target-class/proxyTargetClass to 'true'", "proxies by setting proxy-target-class/proxyTargetClass to 'true'.",
method.getName(), method.getDeclaringClass().getSimpleName())); method.getName(), method.getDeclaringClass().getSimpleName()));
} }
} }
......
...@@ -23,7 +23,7 @@ import javax.validation.ValidatorFactory; ...@@ -23,7 +23,7 @@ import javax.validation.ValidatorFactory;
import org.aopalliance.aop.Advice; import org.aopalliance.aop.Advice;
import org.springframework.aop.Pointcut; import org.springframework.aop.Pointcut;
import org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor; import org.springframework.aop.framework.autoproxy.AbstractBeanFactoryAwareAdvisingPostProcessor;
import org.springframework.aop.support.DefaultPointcutAdvisor; import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut; import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
...@@ -58,7 +58,8 @@ import org.springframework.validation.annotation.Validated; ...@@ -58,7 +58,8 @@ import org.springframework.validation.annotation.Validated;
* @see org.hibernate.validator.method.MethodValidator * @see org.hibernate.validator.method.MethodValidator
*/ */
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class MethodValidationPostProcessor extends AbstractAdvisingBeanPostProcessor implements InitializingBean { public class MethodValidationPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor
implements InitializingBean {
private Class<? extends Annotation> validatedAnnotationType = Validated.class; private Class<? extends Annotation> validatedAnnotationType = Validated.class;
......
...@@ -61,6 +61,7 @@ import static org.junit.Assert.*; ...@@ -61,6 +61,7 @@ import static org.junit.Assert.*;
/** /**
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Juergen Hoeller
*/ */
public class AnnotationDrivenEventListenerTests { public class AnnotationDrivenEventListenerTests {
...@@ -230,8 +231,8 @@ public class AnnotationDrivenEventListenerTests { ...@@ -230,8 +231,8 @@ public class AnnotationDrivenEventListenerTests {
} }
@Test @Test
public void eventListenerWorksWithInterfaceProxy() throws Exception { public void eventListenerWorksWithSimpleInterfaceProxy() throws Exception {
load(ProxyTestBean.class); load(ScopedProxyTestBean.class);
SimpleService proxy = this.context.getBean(SimpleService.class); SimpleService proxy = this.context.getBean(SimpleService.class);
assertTrue("bean should be a proxy", proxy instanceof Advised); assertTrue("bean should be a proxy", proxy instanceof Advised);
...@@ -243,6 +244,20 @@ public class AnnotationDrivenEventListenerTests { ...@@ -243,6 +244,20 @@ public class AnnotationDrivenEventListenerTests {
this.eventCollector.assertTotalEventsCount(1); this.eventCollector.assertTotalEventsCount(1);
} }
@Test
public void eventListenerWorksWithAnnotatedInterfaceProxy() throws Exception {
load(AnnotatedProxyTestBean.class);
AnnotatedSimpleService proxy = this.context.getBean(AnnotatedSimpleService.class);
assertTrue("bean should be a proxy", proxy instanceof Advised);
this.eventCollector.assertNoEventReceived(proxy.getId());
TestEvent event = new TestEvent();
this.context.publishEvent(event);
this.eventCollector.assertEvent(proxy.getId(), event);
this.eventCollector.assertTotalEventsCount(1);
}
@Test @Test
public void eventListenerWorksWithCglibProxy() throws Exception { public void eventListenerWorksWithCglibProxy() throws Exception {
load(CglibProxyTestBean.class); load(CglibProxyTestBean.class);
...@@ -260,13 +275,43 @@ public class AnnotationDrivenEventListenerTests { ...@@ -260,13 +275,43 @@ public class AnnotationDrivenEventListenerTests {
@Test @Test
public void asyncProcessingApplied() throws InterruptedException { public void asyncProcessingApplied() throws InterruptedException {
loadAsync(AsyncEventListener.class); loadAsync(AsyncEventListener.class);
String threadName = Thread.currentThread().getName(); String threadName = Thread.currentThread().getName();
AnotherTestEvent event = new AnotherTestEvent(this, threadName); AnotherTestEvent event = new AnotherTestEvent(this, threadName);
AsyncEventListener listener = this.context.getBean(AsyncEventListener.class); AsyncEventListener listener = this.context.getBean(AsyncEventListener.class);
this.eventCollector.assertNoEventReceived(listener); this.eventCollector.assertNoEventReceived(listener);
this.context.publishEvent(event); this.context.publishEvent(event);
countDownLatch.await(2, TimeUnit.SECONDS);
this.eventCollector.assertEvent(listener, event);
this.eventCollector.assertTotalEventsCount(1);
}
@Test
public void asyncProcessingAppliedWithInterfaceProxy() throws InterruptedException {
doLoad(AsyncConfigurationWithInterfaces.class, SimpleProxyTestBean.class);
String threadName = Thread.currentThread().getName();
AnotherTestEvent event = new AnotherTestEvent(this, threadName);
SimpleService listener = this.context.getBean(SimpleService.class);
this.eventCollector.assertNoEventReceived(listener);
this.context.publishEvent(event);
countDownLatch.await(2, TimeUnit.SECONDS);
this.eventCollector.assertEvent(listener, event);
this.eventCollector.assertTotalEventsCount(1);
}
@Test
public void asyncProcessingAppliedWithScopedProxy() throws InterruptedException {
doLoad(AsyncConfigurationWithInterfaces.class, ScopedProxyTestBean.class);
String threadName = Thread.currentThread().getName();
AnotherTestEvent event = new AnotherTestEvent(this, threadName);
SimpleService listener = this.context.getBean(SimpleService.class);
this.eventCollector.assertNoEventReceived(listener);
this.context.publishEvent(event);
countDownLatch.await(2, TimeUnit.SECONDS); countDownLatch.await(2, TimeUnit.SECONDS);
this.eventCollector.assertEvent(listener, event); this.eventCollector.assertEvent(listener, event);
this.eventCollector.assertTotalEventsCount(1); this.eventCollector.assertTotalEventsCount(1);
...@@ -443,7 +488,6 @@ public class AnnotationDrivenEventListenerTests { ...@@ -443,7 +488,6 @@ public class AnnotationDrivenEventListenerTests {
public CountDownLatch testCountDownLatch() { public CountDownLatch testCountDownLatch() {
return new CountDownLatch(1); return new CountDownLatch(1);
} }
} }
...@@ -530,7 +574,6 @@ public class AnnotationDrivenEventListenerTests { ...@@ -530,7 +574,6 @@ public class AnnotationDrivenEventListenerTests {
} }
return event.content; return event.content;
} }
} }
...@@ -560,6 +603,22 @@ public class AnnotationDrivenEventListenerTests { ...@@ -560,6 +603,22 @@ public class AnnotationDrivenEventListenerTests {
} }
@Component
static class AsyncEventListener extends AbstractTestEventListener {
@Autowired
private CountDownLatch countDownLatch;
@EventListener
@Async
public void handleAsync(AnotherTestEvent event) {
assertTrue(!Thread.currentThread().getName().equals(event.content));
collectEvent(event);
countDownLatch.countDown();
}
}
@Configuration @Configuration
@Import(BasicConfiguration.class) @Import(BasicConfiguration.class)
@EnableAsync(proxyTargetClass = true) @EnableAsync(proxyTargetClass = true)
...@@ -567,33 +626,82 @@ public class AnnotationDrivenEventListenerTests { ...@@ -567,33 +626,82 @@ public class AnnotationDrivenEventListenerTests {
} }
@Configuration
@Import(BasicConfiguration.class)
@EnableAsync(proxyTargetClass = false)
static class AsyncConfigurationWithInterfaces {
}
interface SimpleService extends Identifiable {
void handleIt(TestEvent event);
void handleAsync(AnotherTestEvent event);
}
@Component @Component
static class AsyncEventListener extends AbstractTestEventListener { static class SimpleProxyTestBean extends AbstractIdentifiable implements SimpleService {
@Autowired
private EventCollector eventCollector;
@Autowired @Autowired
private CountDownLatch countDownLatch; private CountDownLatch countDownLatch;
@EventListener
@Override
public void handleIt(TestEvent event) {
eventCollector.addEvent(this, event);
}
@EventListener @EventListener
@Async @Async
public void handleAsync(AnotherTestEvent event) { public void handleAsync(AnotherTestEvent event) {
assertTrue(!Thread.currentThread().getName().equals(event.content)); assertTrue(!Thread.currentThread().getName().equals(event.content));
collectEvent(event); eventCollector.addEvent(this, event);
countDownLatch.countDown(); countDownLatch.countDown();
} }
} }
interface SimpleService extends Identifiable { @Component
@Scope(proxyMode = ScopedProxyMode.INTERFACES)
static class ScopedProxyTestBean extends AbstractIdentifiable implements SimpleService {
@Autowired
private EventCollector eventCollector;
@Autowired
private CountDownLatch countDownLatch;
@EventListener @EventListener
void handleIt(TestEvent event); @Override
public void handleIt(TestEvent event) {
eventCollector.addEvent(this, event);
}
@EventListener
@Async
public void handleAsync(AnotherTestEvent event) {
assertTrue(!Thread.currentThread().getName().equals(event.content));
eventCollector.addEvent(this, event);
countDownLatch.countDown();
}
}
interface AnnotatedSimpleService extends Identifiable {
@EventListener
void handleIt(TestEvent event);
} }
@Component @Component
@Scope(proxyMode = ScopedProxyMode.INTERFACES) @Scope(proxyMode = ScopedProxyMode.INTERFACES)
static class ProxyTestBean extends AbstractIdentifiable implements SimpleService { static class AnnotatedProxyTestBean extends AbstractIdentifiable implements AnnotatedSimpleService {
@Autowired @Autowired
private EventCollector eventCollector; private EventCollector eventCollector;
...@@ -645,7 +753,6 @@ public class AnnotationDrivenEventListenerTests { ...@@ -645,7 +753,6 @@ public class AnnotationDrivenEventListenerTests {
public void handleTimestamp(Long timestamp) { public void handleTimestamp(Long timestamp) {
collectEvent(timestamp); collectEvent(timestamp);
} }
} }
......
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -18,9 +18,8 @@ package org.springframework.dao.annotation; ...@@ -18,9 +18,8 @@ package org.springframework.dao.annotation;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor; import org.springframework.aop.framework.autoproxy.AbstractBeanFactoryAwareAdvisingPostProcessor;
import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import org.springframework.util.Assert; import org.springframework.util.Assert;
...@@ -57,8 +56,7 @@ import org.springframework.util.Assert; ...@@ -57,8 +56,7 @@ import org.springframework.util.Assert;
* @see org.springframework.dao.support.PersistenceExceptionTranslator * @see org.springframework.dao.support.PersistenceExceptionTranslator
*/ */
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class PersistenceExceptionTranslationPostProcessor extends AbstractAdvisingBeanPostProcessor public class PersistenceExceptionTranslationPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor {
implements BeanFactoryAware {
private Class<? extends Annotation> repositoryAnnotationType = Repository.class; private Class<? extends Annotation> repositoryAnnotationType = Repository.class;
...@@ -78,6 +76,8 @@ public class PersistenceExceptionTranslationPostProcessor extends AbstractAdvisi ...@@ -78,6 +76,8 @@ public class PersistenceExceptionTranslationPostProcessor extends AbstractAdvisi
@Override @Override
public void setBeanFactory(BeanFactory beanFactory) { public void setBeanFactory(BeanFactory beanFactory) {
super.setBeanFactory(beanFactory);
if (!(beanFactory instanceof ListableBeanFactory)) { if (!(beanFactory instanceof ListableBeanFactory)) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Cannot use PersistenceExceptionTranslator autodetection without ListableBeanFactory"); "Cannot use PersistenceExceptionTranslator autodetection without ListableBeanFactory");
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册