diff --git a/spring-context/src/main/java/org/springframework/context/event/EventListenerMethodProcessor.java b/spring-context/src/main/java/org/springframework/context/event/EventListenerMethodProcessor.java index d80189fe10a..2db4e2bbcd6 100644 --- a/spring-context/src/main/java/org/springframework/context/event/EventListenerMethodProcessor.java +++ b/spring-context/src/main/java/org/springframework/context/event/EventListenerMethodProcessor.java @@ -28,9 +28,7 @@ import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.aop.SpringProxy; import org.springframework.aop.scope.ScopedProxyUtils; -import org.springframework.aop.support.AopUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanInitializationException; import org.springframework.beans.factory.SmartInitializingSingleton; @@ -48,6 +46,7 @@ import org.springframework.util.ReflectionUtils; * instances. * * @author Stephane Nicoll + * @author Juergen Hoeller * @since 4.2 */ public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware { @@ -61,6 +60,7 @@ public class EventListenerMethodProcessor implements SmartInitializingSingleton, private final Set> nonAnnotatedClasses = Collections.newSetFromMap(new ConcurrentHashMap, Boolean>(64)); + @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { Assert.isTrue(applicationContext instanceof ConfigurableApplicationContext, @@ -69,6 +69,24 @@ public class EventListenerMethodProcessor implements SmartInitializingSingleton, } + @Override + public void afterSingletonsInstantiated() { + List factories = getEventListenerFactories(); + String[] allBeanNames = this.applicationContext.getBeanNamesForType(Object.class); + for (String beanName : allBeanNames) { + if (!ScopedProxyUtils.isScopedTarget(beanName)) { + Class type = this.applicationContext.getType(beanName); + 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} * annotated methods. @@ -81,26 +99,7 @@ public class EventListenerMethodProcessor implements SmartInitializingSingleton, return allFactories; } - @Override - public void afterSingletonsInstantiated() { - List factories = getEventListenerFactories(); - String[] allBeanNames = this.applicationContext.getBeanNamesForType(Object.class); - for (String beanName : allBeanNames) { - if (!ScopedProxyUtils.isScopedTarget(beanName)) { - Class type = this.applicationContext.getType(beanName); - try { - processBean(factories, beanName, type); - } - catch (RuntimeException e) { - throw new BeanInitializationException("Failed to process @EventListener " + - "annotation on bean with name '" + beanName + "'", e); - } - } - } - } - - protected void processBean(List factories, String beanName, final Class type) { - Class targetType = getTargetClass(beanName, type); + protected void processBean(List factories, String beanName, final Class targetType) { if (!this.nonAnnotatedClasses.contains(targetType)) { final Set annotatedMethods = new LinkedHashSet(1); Method[] methods = ReflectionUtils.getUniqueDeclaredMethods(targetType); @@ -111,13 +110,10 @@ public class EventListenerMethodProcessor implements SmartInitializingSingleton, } for (EventListenerFactory factory : factories) { if (factory.supportsMethod(method)) { - if (!type.equals(targetType)) { - method = getProxyMethod(type, method); - } ApplicationListener applicationListener = - factory.createApplicationListener(beanName, type, method); + factory.createApplicationListener(beanName, targetType, method); if (applicationListener instanceof ApplicationListenerMethodAdapter) { - ((ApplicationListenerMethodAdapter)applicationListener) + ((ApplicationListenerMethodAdapter) applicationListener) .init(this.applicationContext, this.evaluator); } this.applicationContext.addApplicationListener(applicationListener); @@ -127,49 +123,19 @@ public class EventListenerMethodProcessor implements SmartInitializingSingleton, } } if (annotatedMethods.isEmpty()) { - this.nonAnnotatedClasses.add(type); + this.nonAnnotatedClasses.add(targetType); if (logger.isTraceEnabled()) { - logger.trace("No @EventListener annotations found on bean class: " + type); + logger.trace("No @EventListener annotations found on bean class: " + targetType); } } else { // Non-empty set of methods if (logger.isDebugEnabled()) { - logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" + beanName + - "': " + annotatedMethods); + logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" + + beanName + "': " + annotatedMethods); } } } } - private Class getTargetClass(String beanName, Class type) { - if (SpringProxy.class.isAssignableFrom(type)) { - Object bean = this.applicationContext.getBean(beanName); - return AopUtils.getTargetClass(bean); - } - else { - return type; - } - } - - private Method getProxyMethod(Class proxyType, Method method) { - try { - // Found a @EventListener method on the target class for this JDK proxy -> - // is it also present on the proxy itself? - return proxyType.getMethod(method.getName(), method.getParameterTypes()); - } - catch (SecurityException ex) { - ReflectionUtils.handleReflectionException(ex); - } - catch (NoSuchMethodException ex) { - throw new IllegalStateException(String.format( - "@EventListener method '%s' found on bean target class '%s', " + - "but not found in any interface(s) for bean JDK proxy. Either " + - "pull the method up to an interface or switch to subclass (CGLIB) " + - "proxies by setting proxy-target-class/proxyTargetClass " + - "attribute to 'true'", method.getName(), method.getDeclaringClass().getSimpleName())); - } - return null; - } - } diff --git a/spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java b/spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java index f68902daf20..0327a35e186 100644 --- a/spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java +++ b/spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java @@ -73,6 +73,7 @@ public class AnnotationDrivenEventListenerTests { private CountDownLatch countDownLatch; // 1 call by default + @After public void closeContext() { if (this.context != null) { @@ -80,6 +81,7 @@ public class AnnotationDrivenEventListenerTests { } } + @Test public void simpleEventJavaConfig() { load(TestEventListener.class); @@ -241,13 +243,6 @@ public class AnnotationDrivenEventListenerTests { this.eventCollector.assertTotalEventsCount(1); } - @Test - public void methodNotAvailableOnProxyIsDetected() throws Exception { - thrown.expect(BeanInitializationException.class); - thrown.expectMessage("handleIt2"); - load(InvalidProxyTestBean.class); - } - @Test public void eventListenerWorksWithCglibProxy() throws Exception { load(CglibProxyTestBean.class); @@ -409,6 +404,7 @@ public class AnnotationDrivenEventListenerTests { assertThat(listener.order, contains("first", "second", "third")); } + private void load(Class... classes) { List> allClasses = new ArrayList<>(); allClasses.add(BasicConfiguration.class); @@ -450,6 +446,7 @@ public class AnnotationDrivenEventListenerTests { } + static abstract class AbstractTestEventListener extends AbstractIdentifiable { @Autowired @@ -458,9 +455,9 @@ public class AnnotationDrivenEventListenerTests { protected void collectEvent(Object content) { this.eventCollector.addEvent(this, content); } - } + @Component static class TestEventListener extends AbstractTestEventListener { @@ -473,15 +470,16 @@ public class AnnotationDrivenEventListenerTests { public void handleString(String content) { collectEvent(content); } - } + @EventListener @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @interface FooListener { } + @Component static class MetaAnnotationListenerTestBean extends AbstractTestEventListener { @@ -491,6 +489,7 @@ public class AnnotationDrivenEventListenerTests { } } + @Component static class ContextEventListener extends AbstractTestEventListener { @@ -501,6 +500,7 @@ public class AnnotationDrivenEventListenerTests { } + @Component static class InvalidMethodSignatureEventListener { @@ -509,6 +509,7 @@ public class AnnotationDrivenEventListenerTests { } } + @Component static class ReplyEventListener extends AbstractTestEventListener { @@ -532,6 +533,7 @@ public class AnnotationDrivenEventListenerTests { } + @Component static class ExceptionEventListener extends AbstractTestEventListener { @@ -557,12 +559,14 @@ public class AnnotationDrivenEventListenerTests { } } + @Configuration @Import(BasicConfiguration.class) @EnableAsync(proxyTargetClass = true) static class AsyncConfiguration { } + @Component static class AsyncEventListener extends AbstractTestEventListener { @@ -578,6 +582,7 @@ public class AnnotationDrivenEventListenerTests { } } + interface SimpleService extends Identifiable { @EventListener @@ -585,6 +590,7 @@ public class AnnotationDrivenEventListenerTests { } + @Component @Scope(proxyMode = ScopedProxyMode.INTERFACES) static class ProxyTestBean extends AbstractIdentifiable implements SimpleService { @@ -598,14 +604,6 @@ public class AnnotationDrivenEventListenerTests { } } - @Component - @Scope(proxyMode = ScopedProxyMode.INTERFACES) - static class InvalidProxyTestBean extends ProxyTestBean { - - @EventListener // does not exist on any interface so it should fail - public void handleIt2(TestEvent event) { - } - } @Component @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS) @@ -617,6 +615,7 @@ public class AnnotationDrivenEventListenerTests { } } + @Component static class GenericEventListener extends AbstractTestEventListener { @@ -626,6 +625,7 @@ public class AnnotationDrivenEventListenerTests { } } + @Component static class ConditionalEventListener extends TestEventListener { @@ -648,6 +648,7 @@ public class AnnotationDrivenEventListenerTests { } + @Component static class OrderedTestListener extends TestEventListener {