Detect event listener methods behind interface proxies as well
Issue: SPR-13650
This commit is contained in:
parent
bc7bcab578
commit
d5efe4f983
|
|
@ -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");
|
* 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
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
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) {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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");
|
* 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
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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
|
||||||
* @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
|
||||||
* <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) {
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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");
|
* 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 {
|
||||||
* <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 {
|
||||||
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;
|
||||||
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;
|
||||||
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;
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
}
|
}
|
||||||
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;
|
||||||
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;
|
||||||
* @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
|
||||||
|
|
||||||
@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;
|
||||||
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;
|
||||||
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;
|
||||||
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,
|
||||||
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,
|
||||||
}
|
}
|
||||||
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,
|
||||||
"@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;
|
||||||
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;
|
||||||
* @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.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
|
* @author Juergen Hoeller
|
||||||
*/
|
*/
|
||||||
public class AnnotationDrivenEventListenerTests {
|
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 {
|
||||||
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 {
|
||||||
@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 {
|
||||||
public CountDownLatch testCountDownLatch() {
|
public CountDownLatch testCountDownLatch() {
|
||||||
return new CountDownLatch(1);
|
return new CountDownLatch(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -530,7 +574,6 @@ public class AnnotationDrivenEventListenerTests {
|
||||||
}
|
}
|
||||||
return event.content;
|
return event.content;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -560,13 +603,6 @@ public class AnnotationDrivenEventListenerTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@Import(BasicConfiguration.class)
|
|
||||||
@EnableAsync(proxyTargetClass = true)
|
|
||||||
static class AsyncConfiguration {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
static class AsyncEventListener extends AbstractTestEventListener {
|
static class AsyncEventListener extends AbstractTestEventListener {
|
||||||
|
|
||||||
|
|
@ -583,17 +619,89 @@ public class AnnotationDrivenEventListenerTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Import(BasicConfiguration.class)
|
||||||
|
@EnableAsync(proxyTargetClass = true)
|
||||||
|
static class AsyncConfiguration {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Import(BasicConfiguration.class)
|
||||||
|
@EnableAsync(proxyTargetClass = false)
|
||||||
|
static class AsyncConfigurationWithInterfaces {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
interface SimpleService extends Identifiable {
|
interface SimpleService extends Identifiable {
|
||||||
|
|
||||||
@EventListener
|
|
||||||
void handleIt(TestEvent event);
|
void handleIt(TestEvent event);
|
||||||
|
|
||||||
|
void handleAsync(AnotherTestEvent event);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Component
|
||||||
|
static class SimpleProxyTestBean extends AbstractIdentifiable implements SimpleService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private EventCollector eventCollector;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CountDownLatch countDownLatch;
|
||||||
|
|
||||||
|
@EventListener
|
||||||
|
@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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@Scope(proxyMode = ScopedProxyMode.INTERFACES)
|
@Scope(proxyMode = ScopedProxyMode.INTERFACES)
|
||||||
static class ProxyTestBean extends AbstractIdentifiable implements SimpleService {
|
static class ScopedProxyTestBean extends AbstractIdentifiable implements SimpleService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private EventCollector eventCollector;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CountDownLatch countDownLatch;
|
||||||
|
|
||||||
|
@EventListener
|
||||||
|
@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
|
||||||
|
@Scope(proxyMode = ScopedProxyMode.INTERFACES)
|
||||||
|
static class AnnotatedProxyTestBean extends AbstractIdentifiable implements AnnotatedSimpleService {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private EventCollector eventCollector;
|
private EventCollector eventCollector;
|
||||||
|
|
@ -645,7 +753,6 @@ public class AnnotationDrivenEventListenerTests {
|
||||||
public void handleTimestamp(Long timestamp) {
|
public void handleTimestamp(Long timestamp) {
|
||||||
collectEvent(timestamp);
|
collectEvent(timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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");
|
* 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;
|
||||||
|
|
||||||
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;
|
||||||
* @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
|
||||||
|
|
||||||
@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");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue