diff --git a/spring-context/src/main/java/org/springframework/context/ApplicationEventPublisher.java b/spring-context/src/main/java/org/springframework/context/ApplicationEventPublisher.java
index 14dd368759f169d3eeadd80574e8f444bd357e3c..b314cf5aa2f950c21aa539336f4a4c0c6b3203e2 100644
--- a/spring-context/src/main/java/org/springframework/context/ApplicationEventPublisher.java
+++ b/spring-context/src/main/java/org/springframework/context/ApplicationEventPublisher.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2005 the original author or authors.
+ * Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,9 +30,9 @@ package org.springframework.context;
public interface ApplicationEventPublisher {
/**
- * Notify all listeners registered with this application of an application
- * event. Events may be framework events (such as RequestHandledEvent)
- * or application-specific events.
+ * Notify all matching listeners registered with this
+ * application of an application event. Events may be framework events
+ * (such as RequestHandledEvent) or application-specific events.
* @param event the event to publish
* @see org.springframework.web.context.support.RequestHandledEvent
*/
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 850e4df4cffb7fa4a79d15e38c674fbebf8cdb39..7fc13089f6b50aa4a02062f284c8300d7c86f94a 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-2014 the original author or authors.
+ * Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.OrderComparator;
+import org.springframework.core.ResolvableType;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
@@ -49,8 +50,9 @@ import org.springframework.util.ObjectUtils;
* Alternative implementations could be more sophisticated in those respects.
*
* @author Juergen Hoeller
+ * @author Stephane Nicoll
* @since 1.2.3
- * @see #getApplicationListeners(ApplicationEvent)
+ * @see #getApplicationListeners(ApplicationEvent, ResolvableType)
* @see SimpleApplicationEventMulticaster
*/
public abstract class AbstractApplicationEventMulticaster
@@ -145,13 +147,16 @@ public abstract class AbstractApplicationEventMulticaster
* event type. Non-matching listeners get excluded early.
* @param event the event to be propagated. Allows for excluding
* non-matching listeners early, based on cached matching information.
+ * @param eventType the event type
* @return a Collection of ApplicationListeners
* @see org.springframework.context.ApplicationListener
*/
- protected Collection> getApplicationListeners(ApplicationEvent event) {
+ protected Collection> getApplicationListeners(
+ ApplicationEvent event, ResolvableType eventType) {
+
Object source = event.getSource();
Class> sourceType = (source != null ? source.getClass() : null);
- ListenerCacheKey cacheKey = new ListenerCacheKey(event.getClass(), sourceType);
+ ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
// Quick check for existing entry on ConcurrentHashMap...
ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
@@ -169,26 +174,28 @@ public abstract class AbstractApplicationEventMulticaster
return retriever.getApplicationListeners();
}
retriever = new ListenerRetriever(true);
- Collection> listeners = retrieveApplicationListeners(event, sourceType, retriever);
+ Collection> listeners =
+ retrieveApplicationListeners(event, eventType, sourceType, retriever);
this.retrieverCache.put(cacheKey, retriever);
return listeners;
}
}
else {
// No ListenerRetriever caching -> no synchronization necessary
- return retrieveApplicationListeners(event, sourceType, null);
+ return retrieveApplicationListeners(event, eventType, sourceType, null);
}
}
/**
* Actually retrieve the application listeners for the given event and source type.
* @param event the application event
+ * @param eventType the event type
* @param sourceType the event source type
* @param retriever the ListenerRetriever, if supposed to populate one (for caching purposes)
* @return the pre-filtered list of application listeners for the given event and source type
*/
private Collection> retrieveApplicationListeners(
- ApplicationEvent event, Class> sourceType, ListenerRetriever retriever) {
+ ApplicationEvent event, ResolvableType eventType, Class> sourceType, ListenerRetriever retriever) {
LinkedList> allListeners = new LinkedList>();
Set> listeners;
@@ -198,7 +205,7 @@ public abstract class AbstractApplicationEventMulticaster
listenerBeans = new LinkedHashSet(this.defaultRetriever.applicationListenerBeans);
}
for (ApplicationListener> listener : listeners) {
- if (supportsEvent(listener, event.getClass(), sourceType)) {
+ if (supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
retriever.applicationListeners.add(listener);
}
@@ -210,10 +217,10 @@ public abstract class AbstractApplicationEventMulticaster
for (String listenerBeanName : listenerBeans) {
try {
Class> listenerType = beanFactory.getType(listenerBeanName);
- if (listenerType == null || supportsEvent(listenerType, event)) {
+ if (listenerType == null || supportsEvent(listenerType, eventType)) {
ApplicationListener> listener =
beanFactory.getBean(listenerBeanName, ApplicationListener.class);
- if (!allListeners.contains(listener) && supportsEvent(listener, event.getClass(), sourceType)) {
+ if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
retriever.applicationListenerBeans.add(listenerBeanName);
}
@@ -236,26 +243,27 @@ public abstract class AbstractApplicationEventMulticaster
* 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.
+ * {@link #supportsEvent(ApplicationListener,ResolvableType, Class)} call afterwards.
* @param listenerType the listener's type as determined by the BeanFactory
- * @param event the event to check
+ * @param eventType the event type 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)) {
+ protected boolean supportsEvent(Class> listenerType, ResolvableType eventType) {
+ if (GenericApplicationListener.class.isAssignableFrom(listenerType)
+ || SmartApplicationListener.class.isAssignableFrom(listenerType)) {
return true;
}
- Class> declaredEventType = GenericApplicationListenerAdapter.resolveDeclaredEventType(listenerType);
- return (declaredEventType == null || declaredEventType.isInstance(event));
+ ResolvableType declaredEventType = GenericApplicationListenerAdapter.resolveDeclaredEventType(listenerType);
+ return (declaredEventType == null || declaredEventType.isAssignableFrom(eventType));
}
/**
* Determine whether the given listener supports the given event.
*
The default implementation detects the {@link SmartApplicationListener}
- * interface. In case of a standard {@link ApplicationListener}, a
- * {@link GenericApplicationListenerAdapter} will be used to introspect
- * the generically declared type of the target listener.
+ * and {@link GenericApplicationListener} interfaces. In case of a standard
+ * {@link ApplicationListener}, a {@link GenericApplicationListenerAdapter}
+ * will be used to introspect the generically declared type of the target listener.
* @param listener the target listener to check
* @param eventType the event type to check against
* @param sourceType the source type to check against
@@ -263,10 +271,10 @@ public abstract class AbstractApplicationEventMulticaster
* for the given event type
*/
protected boolean supportsEvent(ApplicationListener> listener,
- Class extends ApplicationEvent> eventType, Class> sourceType) {
+ ResolvableType eventType, Class> sourceType) {
- SmartApplicationListener smartListener = (listener instanceof SmartApplicationListener ?
- (SmartApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
+ GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
+ (GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
}
@@ -276,11 +284,11 @@ public abstract class AbstractApplicationEventMulticaster
*/
private static class ListenerCacheKey {
- private final Class> eventType;
+ private final ResolvableType eventType;
private final Class> sourceType;
- public ListenerCacheKey(Class> eventType, Class> sourceType) {
+ public ListenerCacheKey(ResolvableType eventType, Class> sourceType) {
this.eventType = eventType;
this.sourceType = sourceType;
}
diff --git a/spring-context/src/main/java/org/springframework/context/event/ApplicationEventMulticaster.java b/spring-context/src/main/java/org/springframework/context/event/ApplicationEventMulticaster.java
index 03bd1ceefe890d729ae95f16bd0effa872a7973f..9805323040068b9bb88f7417311ac609539d938a 100644
--- a/spring-context/src/main/java/org/springframework/context/event/ApplicationEventMulticaster.java
+++ b/spring-context/src/main/java/org/springframework/context/event/ApplicationEventMulticaster.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2009 the original author or authors.
+ * Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ package org.springframework.context.event;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
+import org.springframework.core.ResolvableType;
/**
* Interface to be implemented by objects that can manage a number of
@@ -29,6 +30,7 @@ import org.springframework.context.ApplicationListener;
*
* @author Rod Johnson
* @author Juergen Hoeller
+ * @author Stephane Nicoll
*/
public interface ApplicationEventMulticaster {
@@ -65,8 +67,19 @@ public interface ApplicationEventMulticaster {
/**
* Multicast the given application event to appropriate listeners.
+ *
Consider using {@link #multicastEvent(ApplicationEvent, ResolvableType)}
+ * if possible as it provides a better support for generics-based events.
* @param event the event to multicast
*/
void multicastEvent(ApplicationEvent event);
+ /**
+ * Multicast the given application event to appropriate listeners.
+ *
If the {@code eventType} is {@code null}, a default type is built
+ * based on the {@code event} instance.
+ * @param event the event to multicast
+ * @param eventType the type of event (can be null)
+ */
+ void multicastEvent(ApplicationEvent event, ResolvableType eventType);
+
}
diff --git a/spring-context/src/main/java/org/springframework/context/event/GenericApplicationListener.java b/spring-context/src/main/java/org/springframework/context/event/GenericApplicationListener.java
new file mode 100644
index 0000000000000000000000000000000000000000..cfe7f457a948a38b19335456145b946abfe82c38
--- /dev/null
+++ b/spring-context/src/main/java/org/springframework/context/event/GenericApplicationListener.java
@@ -0,0 +1,46 @@
+/*
+ * 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.context.event;
+
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ApplicationListener;
+import org.springframework.core.Ordered;
+import org.springframework.core.ResolvableType;
+
+/**
+ * Extended variant of the standard {@link ApplicationListener} interface,
+ * exposing further metadata such as the supported event type.
+ *
+ *
As of Spring Framework 4.2, supersedes {@link SmartApplicationListener} with
+ * proper handling of generics-based event.
+ *
+ * @author Stephane Nicoll
+ * @since 4.2
+ */
+public interface GenericApplicationListener extends ApplicationListener, Ordered {
+
+ /**
+ * Determine whether this listener actually supports the given event type.
+ */
+ boolean supportsEventType(ResolvableType eventType);
+
+ /**
+ * Determine whether this listener actually supports the given source type.
+ */
+ boolean supportsSourceType(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 0d46b5980ae5aaaae0acb44101715f3ebb675532..9362ac78755e9a4439c7f1eb400f2f5883af727b 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-2014 the original author or authors.
+ * Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,22 +19,24 @@ package org.springframework.context.event;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
-import org.springframework.core.GenericTypeResolver;
import org.springframework.core.Ordered;
+import org.springframework.core.ResolvableType;
import org.springframework.util.Assert;
/**
- * {@link SmartApplicationListener} adapter that determines supported event types
+ * {@link GenericApplicationListener} adapter that determines supported event types
* through introspecting the generically declared type of the target listener.
*
* @author Juergen Hoeller
+ * @author Stephane Nicoll
* @since 3.0
* @see org.springframework.context.ApplicationListener#onApplicationEvent
*/
-public class GenericApplicationListenerAdapter implements SmartApplicationListener {
+public class GenericApplicationListenerAdapter implements GenericApplicationListener {
private final ApplicationListener delegate;
+ private final ResolvableType declaredEventType;
/**
* Create a new GenericApplicationListener for the given delegate.
@@ -44,6 +46,7 @@ public class GenericApplicationListenerAdapter implements SmartApplicationListen
public GenericApplicationListenerAdapter(ApplicationListener> delegate) {
Assert.notNull(delegate, "Delegate listener must not be null");
this.delegate = (ApplicationListener) delegate;
+ this.declaredEventType = resolveDeclaredEventType(this.delegate);
}
@@ -53,20 +56,25 @@ public class GenericApplicationListenerAdapter implements SmartApplicationListen
}
@Override
- public boolean supportsEventType(Class extends ApplicationEvent> eventType) {
- Class> declaredEventType = resolveDeclaredEventType(this.delegate.getClass());
- if (declaredEventType == null || declaredEventType.equals(ApplicationEvent.class)) {
- Class> targetClass = AopUtils.getTargetClass(this.delegate);
- if (targetClass != this.delegate.getClass()) {
- declaredEventType = resolveDeclaredEventType(targetClass);
- }
+ @SuppressWarnings("unchecked")
+ public boolean supportsEventType(ResolvableType eventType) {
+ if (this.delegate instanceof SmartApplicationListener) {
+ Class extends ApplicationEvent> eventClass = (Class extends ApplicationEvent>) eventType.getRawClass();
+ return ((SmartApplicationListener) this.delegate).supportsEventType(eventClass);
+ }
+ else {
+ return (this.declaredEventType == null || this.declaredEventType.isAssignableFrom(eventType));
}
- return (declaredEventType == null || declaredEventType.isAssignableFrom(eventType));
}
@Override
public boolean supportsSourceType(Class> sourceType) {
- return true;
+ if (this.delegate instanceof SmartApplicationListener) {
+ return ((SmartApplicationListener) this.delegate).supportsSourceType(sourceType);
+ }
+ else {
+ return true;
+ }
}
@Override
@@ -74,9 +82,25 @@ public class GenericApplicationListenerAdapter implements SmartApplicationListen
return (this.delegate instanceof Ordered ? ((Ordered) this.delegate).getOrder() : Ordered.LOWEST_PRECEDENCE);
}
+ static ResolvableType resolveDeclaredEventType(Class> listenerType) {
+ ResolvableType resolvableType = ResolvableType.forClass(listenerType).as(ApplicationListener.class);
+ if (resolvableType == null || !resolvableType.hasGenerics()) {
+ return null;
+ }
+ return resolvableType.getGeneric();
+ }
+
+ private static ResolvableType resolveDeclaredEventType(ApplicationListener listener) {
+ ResolvableType declaredEventType = resolveDeclaredEventType(listener.getClass());
+ if (declaredEventType == null || declaredEventType.isAssignableFrom(
+ ResolvableType.forClass(ApplicationEvent.class))) {
- static Class> resolveDeclaredEventType(Class> listenerType) {
- return GenericTypeResolver.resolveTypeArgument(listenerType, ApplicationListener.class);
+ Class> targetClass = AopUtils.getTargetClass(listener);
+ if (targetClass != listener.getClass()) {
+ declaredEventType = resolveDeclaredEventType(targetClass);
+ }
+ }
+ return declaredEventType;
}
}
diff --git a/spring-context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java b/spring-context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java
index d044aaf94658ab1256582789d82ac2947d755776..28587f21f4964f94e19dc2589a3d53cb7f7b186d 100644
--- a/spring-context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java
+++ b/spring-context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ import java.util.concurrent.Executor;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
+import org.springframework.core.ResolvableType;
import org.springframework.util.ErrorHandler;
/**
@@ -38,6 +39,7 @@ import org.springframework.util.ErrorHandler;
*
* @author Rod Johnson
* @author Juergen Hoeller
+ * @author Stephane Nicoll
* @see #setTaskExecutor
*/
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
@@ -113,8 +115,14 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM
@Override
- public void multicastEvent(final ApplicationEvent event) {
- for (final ApplicationListener> listener : getApplicationListeners(event)) {
+ public void multicastEvent(ApplicationEvent event) {
+ multicastEvent(event, resolveDefaultEventType(event));
+ }
+
+ @Override
+ public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
+ ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
+ for (final ApplicationListener> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(new Runnable() {
@@ -130,6 +138,10 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM
}
}
+ private ResolvableType resolveDefaultEventType(ApplicationEvent event) {
+ return ResolvableType.forType(event.getClass());
+ }
+
/**
* Invoke the given listener with the given event.
* @param listener the ApplicationListener to invoke
diff --git a/spring-context/src/main/java/org/springframework/context/event/SmartApplicationListener.java b/spring-context/src/main/java/org/springframework/context/event/SmartApplicationListener.java
index 9fe22adaf1e5908bda0d139c20013122604ead55..5d509207541e02ad6cc5a992b6ea3d973037590d 100644
--- a/spring-context/src/main/java/org/springframework/context/event/SmartApplicationListener.java
+++ b/spring-context/src/main/java/org/springframework/context/event/SmartApplicationListener.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2009 the original author or authors.
+ * Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,8 +24,13 @@ import org.springframework.core.Ordered;
* Extended variant of the standard {@link ApplicationListener} interface,
* exposing further metadata such as the supported event type.
*
+ *
Users are strongly advised to use the {@link GenericApplicationListener}
+ * interface instead as it provides an improved detection of generics-based
+ * event types.
+ *
* @author Juergen Hoeller
* @since 3.0
+ * @see GenericApplicationListener
*/
public interface SmartApplicationListener extends ApplicationListener, Ordered {
diff --git a/spring-context/src/main/java/org/springframework/context/event/SourceFilteringListener.java b/spring-context/src/main/java/org/springframework/context/event/SourceFilteringListener.java
index 93bf92f756bab1f3834662aa6a84aa836d74ee5b..6143b48483115e95c2e2ccc5e03981972c931094 100644
--- a/spring-context/src/main/java/org/springframework/context/event/SourceFilteringListener.java
+++ b/spring-context/src/main/java/org/springframework/context/event/SourceFilteringListener.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ package org.springframework.context.event;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.Ordered;
+import org.springframework.core.ResolvableType;
/**
* {@link org.springframework.context.ApplicationListener} decorator that filters
@@ -29,13 +30,14 @@ import org.springframework.core.Ordered;
* method instead of specifying a delegate listener.
*
* @author Juergen Hoeller
+ * @author Stephane Nicoll
* @since 2.0.5
*/
-public class SourceFilteringListener implements SmartApplicationListener {
+public class SourceFilteringListener implements GenericApplicationListener {
private final Object source;
- private SmartApplicationListener delegate;
+ private GenericApplicationListener delegate;
/**
@@ -47,8 +49,8 @@ public class SourceFilteringListener implements SmartApplicationListener {
*/
public SourceFilteringListener(Object source, ApplicationListener> delegate) {
this.source = source;
- this.delegate = (delegate instanceof SmartApplicationListener ?
- (SmartApplicationListener) delegate : new GenericApplicationListenerAdapter(delegate));
+ this.delegate = (delegate instanceof GenericApplicationListener ?
+ (GenericApplicationListener) delegate : new GenericApplicationListenerAdapter(delegate));
}
/**
@@ -71,7 +73,7 @@ public class SourceFilteringListener implements SmartApplicationListener {
}
@Override
- public boolean supportsEventType(Class extends ApplicationEvent> eventType) {
+ public boolean supportsEventType(ResolvableType eventType) {
return (this.delegate == null || this.delegate.supportsEventType(eventType));
}
diff --git a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
index 75e5e3d873f750a70ee3187eea8dd18b173f1b64..aac94c976df38c1d906d50f40dcfec50d4fc5747 100644
--- a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
+++ b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -63,6 +63,7 @@ import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.context.expression.StandardBeanExpressionResolver;
import org.springframework.context.weaving.LoadTimeWeaverAware;
import org.springframework.context.weaving.LoadTimeWeaverAwareProcessor;
+import org.springframework.core.ResolvableType;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
@@ -107,6 +108,7 @@ import org.springframework.util.ObjectUtils;
* @author Rod Johnson
* @author Juergen Hoeller
* @author Mark Fisher
+ * @author Stephane Nicoll
* @since January 21, 2001
* @see #refreshBeanFactory
* @see #getBeanFactory
@@ -324,13 +326,22 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
*/
@Override
public void publishEvent(ApplicationEvent event) {
+ publishEvent(event, null);
+ }
+
+ protected void publishEvent(ApplicationEvent event, ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Publishing event in " + getDisplayName() + ": " + event);
}
- getApplicationEventMulticaster().multicastEvent(event);
+ getApplicationEventMulticaster().multicastEvent(event, eventType);
if (this.parent != null) {
- this.parent.publishEvent(event);
+ if (this.parent instanceof AbstractApplicationContext) {
+ ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
+ }
+ else {
+ this.parent.publishEvent(event);
+ }
}
}
diff --git a/spring-context/src/test/java/org/springframework/context/event/AbstractApplicationEventListenerTests.java b/spring-context/src/test/java/org/springframework/context/event/AbstractApplicationEventListenerTests.java
new file mode 100644
index 0000000000000000000000000000000000000000..98dd3ea1cad34b4e753b5536c0ed5d32fa5a881b
--- /dev/null
+++ b/spring-context/src/test/java/org/springframework/context/event/AbstractApplicationEventListenerTests.java
@@ -0,0 +1,129 @@
+/*
+ * 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.context.event;
+
+import java.io.IOException;
+
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ApplicationListener;
+import org.springframework.core.ResolvableType;
+
+/**
+ * @author Stephane Nicoll
+ */
+@SuppressWarnings("serial")
+public abstract class AbstractApplicationEventListenerTests {
+
+ protected ResolvableType getGenericApplicationEventType(String fieldName) {
+ try {
+ return ResolvableType.forField(GenericApplicationEvents.class.getField(fieldName));
+ }
+ catch (NoSuchFieldException e) {
+ throw new IllegalStateException("No such field on Events '" + fieldName + "'");
+ }
+ }
+
+ protected static class GenericApplicationEvent
+ extends ApplicationEvent {
+
+ private final T payload;
+
+ public GenericApplicationEvent(Object source, T payload) {
+ super(source);
+ this.payload = payload;
+ }
+
+ public T getPayload() {
+ return payload;
+ }
+
+ }
+
+ protected static class StringEvent extends GenericApplicationEvent {
+
+ public StringEvent(Object source, String payload) {
+ super(source, payload);
+ }
+ }
+
+ protected static class LongEvent extends GenericApplicationEvent {
+
+ public LongEvent(Object source, Long payload) {
+ super(source, payload);
+ }
+ }
+
+ protected GenericApplicationEvent createGenericEvent(T payload) {
+ return new GenericApplicationEvent<>(this, payload);
+ }
+
+
+ static class GenericEventListener implements ApplicationListener> {
+ @Override
+ public void onApplicationEvent(GenericApplicationEvent> event) {
+
+ }
+ }
+
+ static class ObjectEventListener implements ApplicationListener> {
+ @Override
+ public void onApplicationEvent(GenericApplicationEvent