diff --git a/spring-context/src/main/java/org/springframework/context/event/AbstractApplicationEventMulticaster.java b/spring-context/src/main/java/org/springframework/context/event/AbstractApplicationEventMulticaster.java index a4940e0e9a..43ae8d195e 100644 --- a/spring-context/src/main/java/org/springframework/context/event/AbstractApplicationEventMulticaster.java +++ b/spring-context/src/main/java/org/springframework/context/event/AbstractApplicationEventMulticaster.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -162,10 +162,14 @@ public abstract class AbstractApplicationEventMulticaster implements Application BeanFactory beanFactory = getBeanFactory(); for (String listenerBeanName : listenerBeans) { try { - ApplicationListener> listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class); - if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) { - retriever.applicationListenerBeans.add(listenerBeanName); - allListeners.add(listener); + Class> listenerType = beanFactory.getType(listenerBeanName); + if (listenerType == null || supportsEvent(listenerType, event)) { + ApplicationListener> listener = + beanFactory.getBean(listenerBeanName, ApplicationListener.class); + if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) { + retriever.applicationListenerBeans.add(listenerBeanName); + allListeners.add(listener); + } } } catch (NoSuchBeanDefinitionException ex) { @@ -180,6 +184,25 @@ public abstract class AbstractApplicationEventMulticaster implements Application } } + /** + * Filter a listener early through checking its generically declared event + * type before trying to instantiate it. + *
If this method returns {@code true} for a given listener as a first pass, + * the listener instance will get retrieved and fully evaluated through a + * {@link #supportsEvent(ApplicationListener, Class, Class)} call afterwards. + * @param listenerType the listener's type as determined by the BeanFactory + * @param event the event to check + * @return whether the given listener should be included in the candidates + * for the given event type + */ + protected boolean supportsEvent(Class> listenerType, ApplicationEvent event) { + if (SmartApplicationListener.class.isAssignableFrom(listenerType)) { + return true; + } + Class> declaredEventType = GenericApplicationListenerAdapter.resolveDeclaredEventType(listenerType); + return (declaredEventType == null || declaredEventType.isInstance(event)); + } + /** * Determine whether the given listener supports the given event. *
The default implementation detects the {@link SmartApplicationListener} @@ -189,8 +212,8 @@ public abstract class AbstractApplicationEventMulticaster implements Application * @param listener the target listener to check * @param eventType the event type to check against * @param sourceType the source type to check against - * @return whether the given listener should be included in the - * candidates for the given event type + * @return whether the given listener should be included in the candidates + * for the given event type */ protected boolean supportsEvent(ApplicationListener> listener, Class extends ApplicationEvent> eventType, Class> sourceType) { diff --git a/spring-context/src/main/java/org/springframework/context/event/GenericApplicationListenerAdapter.java b/spring-context/src/main/java/org/springframework/context/event/GenericApplicationListenerAdapter.java index 5180e1ab78..0d46b5980a 100644 --- a/spring-context/src/main/java/org/springframework/context/event/GenericApplicationListenerAdapter.java +++ b/spring-context/src/main/java/org/springframework/context/event/GenericApplicationListenerAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -54,14 +54,14 @@ public class GenericApplicationListenerAdapter implements SmartApplicationListen @Override public boolean supportsEventType(Class extends ApplicationEvent> eventType) { - Class> typeArg = GenericTypeResolver.resolveTypeArgument(this.delegate.getClass(), ApplicationListener.class); - if (typeArg == null || typeArg.equals(ApplicationEvent.class)) { + Class> declaredEventType = resolveDeclaredEventType(this.delegate.getClass()); + if (declaredEventType == null || declaredEventType.equals(ApplicationEvent.class)) { Class> targetClass = AopUtils.getTargetClass(this.delegate); if (targetClass != this.delegate.getClass()) { - typeArg = GenericTypeResolver.resolveTypeArgument(targetClass, ApplicationListener.class); + declaredEventType = resolveDeclaredEventType(targetClass); } } - return (typeArg == null || typeArg.isAssignableFrom(eventType)); + return (declaredEventType == null || declaredEventType.isAssignableFrom(eventType)); } @Override @@ -74,4 +74,9 @@ public class GenericApplicationListenerAdapter implements SmartApplicationListen return (this.delegate instanceof Ordered ? ((Ordered) this.delegate).getOrder() : Ordered.LOWEST_PRECEDENCE); } + + static Class> resolveDeclaredEventType(Class> listenerType) { + return GenericTypeResolver.resolveTypeArgument(listenerType, ApplicationListener.class); + } + } diff --git a/spring-context/src/test/java/org/springframework/context/event/ApplicationContextEventTests.java b/spring-context/src/test/java/org/springframework/context/event/ApplicationContextEventTests.java index 2bf1053227..a882ba8e9f 100644 --- a/spring-context/src/test/java/org/springframework/context/event/ApplicationContextEventTests.java +++ b/spring-context/src/test/java/org/springframework/context/event/ApplicationContextEventTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -110,14 +110,18 @@ public class ApplicationContextEventTests { context.registerBeanDefinition("listener1", new RootBeanDefinition(MyOrderedListener1.class)); RootBeanDefinition listener2 = new RootBeanDefinition(MyOrderedListener2.class); listener2.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference("listener1")); + listener2.setLazyInit(true); context.registerBeanDefinition("listener2", listener2); context.refresh(); + assertFalse(context.getDefaultListableBeanFactory().containsSingleton("listener2")); MyOrderedListener1 listener1 = context.getBean("listener1", MyOrderedListener1.class); - MyEvent event1 = new MyEvent(context); + MyOtherEvent event1 = new MyOtherEvent(context); context.publishEvent(event1); - MyOtherEvent event2 = new MyOtherEvent(context); + assertFalse(context.getDefaultListableBeanFactory().containsSingleton("listener2")); + MyEvent event2 = new MyEvent(context); context.publishEvent(event2); + assertTrue(context.getDefaultListableBeanFactory().containsSingleton("listener2")); MyEvent event3 = new MyEvent(context); context.publishEvent(event3); MyOtherEvent event4 = new MyOtherEvent(context);