Support for generics-based events
Update the event publishing infrastructure to support generics-based
events, that is support ApplicationListener implementations that define
a generic event, something like:
public class MyListener
implements ApplicationListener<GenericEvent<String>> { ... }
This listener should only receive events that are matching the generic
signature, for instance:
public class StringEvent extends GenericEvent<String> { ... }
Note that because of type erasure, publishing an event that defines the
generic type at the instance level will not work. In other words,
publishing "new GenericEvent<String>" will not work as expected as type
erasure will define it as GenericEvent<?>.
To support this feature, use the new GenericApplicationListener that
supersedes SmartApplicationListener to handle generics-based even types via
`supportsEventType` that takes a ResolvableType instance instead of the
simple Class of the event. ApplicationEventMulticaster has an additional
method to multicast an event based on the event and its ResolvableType.
Issue: SPR-8201
This commit is contained in:
parent
f84c458aba
commit
6d6422acde
|
|
@ -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");
|
* 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.
|
||||||
|
|
@ -30,9 +30,9 @@ package org.springframework.context;
|
||||||
public interface ApplicationEventPublisher {
|
public interface ApplicationEventPublisher {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify all listeners registered with this application of an application
|
* Notify all <strong>matching</strong> listeners registered with this
|
||||||
* event. Events may be framework events (such as RequestHandledEvent)
|
* application of an application event. Events may be framework events
|
||||||
* or application-specific events.
|
* (such as RequestHandledEvent) or application-specific events.
|
||||||
* @param event the event to publish
|
* @param event the event to publish
|
||||||
* @see org.springframework.web.context.support.RequestHandledEvent
|
* @see org.springframework.web.context.support.RequestHandledEvent
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -31,6 +31,7 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||||
import org.springframework.context.ApplicationEvent;
|
import org.springframework.context.ApplicationEvent;
|
||||||
import org.springframework.context.ApplicationListener;
|
import org.springframework.context.ApplicationListener;
|
||||||
import org.springframework.core.OrderComparator;
|
import org.springframework.core.OrderComparator;
|
||||||
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
|
|
||||||
|
|
@ -49,8 +50,9 @@ import org.springframework.util.ObjectUtils;
|
||||||
* Alternative implementations could be more sophisticated in those respects.
|
* Alternative implementations could be more sophisticated in those respects.
|
||||||
*
|
*
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
|
* @author Stephane Nicoll
|
||||||
* @since 1.2.3
|
* @since 1.2.3
|
||||||
* @see #getApplicationListeners(ApplicationEvent)
|
* @see #getApplicationListeners(ApplicationEvent, ResolvableType)
|
||||||
* @see SimpleApplicationEventMulticaster
|
* @see SimpleApplicationEventMulticaster
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractApplicationEventMulticaster
|
public abstract class AbstractApplicationEventMulticaster
|
||||||
|
|
@ -145,13 +147,16 @@ public abstract class AbstractApplicationEventMulticaster
|
||||||
* event type. Non-matching listeners get excluded early.
|
* event type. Non-matching listeners get excluded early.
|
||||||
* @param event the event to be propagated. Allows for excluding
|
* @param event the event to be propagated. Allows for excluding
|
||||||
* non-matching listeners early, based on cached matching information.
|
* non-matching listeners early, based on cached matching information.
|
||||||
|
* @param eventType the event type
|
||||||
* @return a Collection of ApplicationListeners
|
* @return a Collection of ApplicationListeners
|
||||||
* @see org.springframework.context.ApplicationListener
|
* @see org.springframework.context.ApplicationListener
|
||||||
*/
|
*/
|
||||||
protected Collection<ApplicationListener<?>> getApplicationListeners(ApplicationEvent event) {
|
protected Collection<ApplicationListener<?>> getApplicationListeners(
|
||||||
|
ApplicationEvent event, ResolvableType eventType) {
|
||||||
|
|
||||||
Object source = event.getSource();
|
Object source = event.getSource();
|
||||||
Class<?> sourceType = (source != null ? source.getClass() : null);
|
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...
|
// Quick check for existing entry on ConcurrentHashMap...
|
||||||
ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
|
ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
|
||||||
|
|
@ -169,26 +174,28 @@ public abstract class AbstractApplicationEventMulticaster
|
||||||
return retriever.getApplicationListeners();
|
return retriever.getApplicationListeners();
|
||||||
}
|
}
|
||||||
retriever = new ListenerRetriever(true);
|
retriever = new ListenerRetriever(true);
|
||||||
Collection<ApplicationListener<?>> listeners = retrieveApplicationListeners(event, sourceType, retriever);
|
Collection<ApplicationListener<?>> listeners =
|
||||||
|
retrieveApplicationListeners(event, eventType, sourceType, retriever);
|
||||||
this.retrieverCache.put(cacheKey, retriever);
|
this.retrieverCache.put(cacheKey, retriever);
|
||||||
return listeners;
|
return listeners;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// No ListenerRetriever caching -> no synchronization necessary
|
// 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.
|
* Actually retrieve the application listeners for the given event and source type.
|
||||||
* @param event the application event
|
* @param event the application event
|
||||||
|
* @param eventType the event type
|
||||||
* @param sourceType the event source type
|
* @param sourceType the event source type
|
||||||
* @param retriever the ListenerRetriever, if supposed to populate one (for caching purposes)
|
* @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
|
* @return the pre-filtered list of application listeners for the given event and source type
|
||||||
*/
|
*/
|
||||||
private Collection<ApplicationListener<?>> retrieveApplicationListeners(
|
private Collection<ApplicationListener<?>> retrieveApplicationListeners(
|
||||||
ApplicationEvent event, Class<?> sourceType, ListenerRetriever retriever) {
|
ApplicationEvent event, ResolvableType eventType, Class<?> sourceType, ListenerRetriever retriever) {
|
||||||
|
|
||||||
LinkedList<ApplicationListener<?>> allListeners = new LinkedList<ApplicationListener<?>>();
|
LinkedList<ApplicationListener<?>> allListeners = new LinkedList<ApplicationListener<?>>();
|
||||||
Set<ApplicationListener<?>> listeners;
|
Set<ApplicationListener<?>> listeners;
|
||||||
|
|
@ -198,7 +205,7 @@ public abstract class AbstractApplicationEventMulticaster
|
||||||
listenerBeans = new LinkedHashSet<String>(this.defaultRetriever.applicationListenerBeans);
|
listenerBeans = new LinkedHashSet<String>(this.defaultRetriever.applicationListenerBeans);
|
||||||
}
|
}
|
||||||
for (ApplicationListener<?> listener : listeners) {
|
for (ApplicationListener<?> listener : listeners) {
|
||||||
if (supportsEvent(listener, event.getClass(), sourceType)) {
|
if (supportsEvent(listener, eventType, sourceType)) {
|
||||||
if (retriever != null) {
|
if (retriever != null) {
|
||||||
retriever.applicationListeners.add(listener);
|
retriever.applicationListeners.add(listener);
|
||||||
}
|
}
|
||||||
|
|
@ -210,10 +217,10 @@ public abstract class AbstractApplicationEventMulticaster
|
||||||
for (String listenerBeanName : listenerBeans) {
|
for (String listenerBeanName : listenerBeans) {
|
||||||
try {
|
try {
|
||||||
Class<?> listenerType = beanFactory.getType(listenerBeanName);
|
Class<?> listenerType = beanFactory.getType(listenerBeanName);
|
||||||
if (listenerType == null || supportsEvent(listenerType, event)) {
|
if (listenerType == null || supportsEvent(listenerType, eventType)) {
|
||||||
ApplicationListener<?> listener =
|
ApplicationListener<?> listener =
|
||||||
beanFactory.getBean(listenerBeanName, ApplicationListener.class);
|
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) {
|
if (retriever != null) {
|
||||||
retriever.applicationListenerBeans.add(listenerBeanName);
|
retriever.applicationListenerBeans.add(listenerBeanName);
|
||||||
}
|
}
|
||||||
|
|
@ -236,26 +243,27 @@ public abstract class AbstractApplicationEventMulticaster
|
||||||
* type before trying to instantiate it.
|
* type before trying to instantiate it.
|
||||||
* <p>If this method returns {@code true} for a given listener as a first pass,
|
* <p>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
|
* 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 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
|
* @return whether the given listener should be included in the candidates
|
||||||
* for the given event type
|
* for the given event type
|
||||||
*/
|
*/
|
||||||
protected boolean supportsEvent(Class<?> listenerType, ApplicationEvent event) {
|
protected boolean supportsEvent(Class<?> listenerType, ResolvableType eventType) {
|
||||||
if (SmartApplicationListener.class.isAssignableFrom(listenerType)) {
|
if (GenericApplicationListener.class.isAssignableFrom(listenerType)
|
||||||
|
|| SmartApplicationListener.class.isAssignableFrom(listenerType)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Class<?> declaredEventType = GenericApplicationListenerAdapter.resolveDeclaredEventType(listenerType);
|
ResolvableType declaredEventType = GenericApplicationListenerAdapter.resolveDeclaredEventType(listenerType);
|
||||||
return (declaredEventType == null || declaredEventType.isInstance(event));
|
return (declaredEventType == null || declaredEventType.isAssignableFrom(eventType));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine whether the given listener supports the given event.
|
* Determine whether the given listener supports the given event.
|
||||||
* <p>The default implementation detects the {@link SmartApplicationListener}
|
* <p>The default implementation detects the {@link SmartApplicationListener}
|
||||||
* interface. In case of a standard {@link ApplicationListener}, a
|
* and {@link GenericApplicationListener} interfaces. In case of a standard
|
||||||
* {@link GenericApplicationListenerAdapter} will be used to introspect
|
* {@link ApplicationListener}, a {@link GenericApplicationListenerAdapter}
|
||||||
* the generically declared type of the target listener.
|
* will be used to introspect the generically declared type of the target listener.
|
||||||
* @param listener the target listener to check
|
* @param listener the target listener to check
|
||||||
* @param eventType the event type to check against
|
* @param eventType the event type to check against
|
||||||
* @param sourceType the source 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
|
* for the given event type
|
||||||
*/
|
*/
|
||||||
protected boolean supportsEvent(ApplicationListener<?> listener,
|
protected boolean supportsEvent(ApplicationListener<?> listener,
|
||||||
Class<? extends ApplicationEvent> eventType, Class<?> sourceType) {
|
ResolvableType eventType, Class<?> sourceType) {
|
||||||
|
|
||||||
SmartApplicationListener smartListener = (listener instanceof SmartApplicationListener ?
|
GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
|
||||||
(SmartApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
|
(GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
|
||||||
return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
|
return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -276,11 +284,11 @@ public abstract class AbstractApplicationEventMulticaster
|
||||||
*/
|
*/
|
||||||
private static class ListenerCacheKey {
|
private static class ListenerCacheKey {
|
||||||
|
|
||||||
private final Class<?> eventType;
|
private final ResolvableType eventType;
|
||||||
|
|
||||||
private final Class<?> sourceType;
|
private final Class<?> sourceType;
|
||||||
|
|
||||||
public ListenerCacheKey(Class<?> eventType, Class<?> sourceType) {
|
public ListenerCacheKey(ResolvableType eventType, Class<?> sourceType) {
|
||||||
this.eventType = eventType;
|
this.eventType = eventType;
|
||||||
this.sourceType = sourceType;
|
this.sourceType = sourceType;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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");
|
* 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,6 +18,7 @@ package org.springframework.context.event;
|
||||||
|
|
||||||
import org.springframework.context.ApplicationEvent;
|
import org.springframework.context.ApplicationEvent;
|
||||||
import org.springframework.context.ApplicationListener;
|
import org.springframework.context.ApplicationListener;
|
||||||
|
import org.springframework.core.ResolvableType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface to be implemented by objects that can manage a number of
|
* 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 Rod Johnson
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
|
* @author Stephane Nicoll
|
||||||
*/
|
*/
|
||||||
public interface ApplicationEventMulticaster {
|
public interface ApplicationEventMulticaster {
|
||||||
|
|
||||||
|
|
@ -65,8 +67,19 @@ public interface ApplicationEventMulticaster {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Multicast the given application event to appropriate listeners.
|
* Multicast the given application event to appropriate listeners.
|
||||||
|
* <p>Consider using {@link #multicastEvent(ApplicationEvent, ResolvableType)}
|
||||||
|
* if possible as it provides a better support for generics-based events.
|
||||||
* @param event the event to multicast
|
* @param event the event to multicast
|
||||||
*/
|
*/
|
||||||
void multicastEvent(ApplicationEvent event);
|
void multicastEvent(ApplicationEvent event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multicast the given application event to appropriate listeners.
|
||||||
|
* <p>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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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<ApplicationEvent>, 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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -19,22 +19,24 @@ package org.springframework.context.event;
|
||||||
import org.springframework.aop.support.AopUtils;
|
import org.springframework.aop.support.AopUtils;
|
||||||
import org.springframework.context.ApplicationEvent;
|
import org.springframework.context.ApplicationEvent;
|
||||||
import org.springframework.context.ApplicationListener;
|
import org.springframework.context.ApplicationListener;
|
||||||
import org.springframework.core.GenericTypeResolver;
|
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.util.Assert;
|
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.
|
* through introspecting the generically declared type of the target listener.
|
||||||
*
|
*
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
|
* @author Stephane Nicoll
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
* @see org.springframework.context.ApplicationListener#onApplicationEvent
|
* @see org.springframework.context.ApplicationListener#onApplicationEvent
|
||||||
*/
|
*/
|
||||||
public class GenericApplicationListenerAdapter implements SmartApplicationListener {
|
public class GenericApplicationListenerAdapter implements GenericApplicationListener {
|
||||||
|
|
||||||
private final ApplicationListener<ApplicationEvent> delegate;
|
private final ApplicationListener<ApplicationEvent> delegate;
|
||||||
|
|
||||||
|
private final ResolvableType declaredEventType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new GenericApplicationListener for the given delegate.
|
* Create a new GenericApplicationListener for the given delegate.
|
||||||
|
|
@ -44,6 +46,7 @@ public class GenericApplicationListenerAdapter implements SmartApplicationListen
|
||||||
public GenericApplicationListenerAdapter(ApplicationListener<?> delegate) {
|
public GenericApplicationListenerAdapter(ApplicationListener<?> delegate) {
|
||||||
Assert.notNull(delegate, "Delegate listener must not be null");
|
Assert.notNull(delegate, "Delegate listener must not be null");
|
||||||
this.delegate = (ApplicationListener<ApplicationEvent>) delegate;
|
this.delegate = (ApplicationListener<ApplicationEvent>) delegate;
|
||||||
|
this.declaredEventType = resolveDeclaredEventType(this.delegate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -53,20 +56,25 @@ public class GenericApplicationListenerAdapter implements SmartApplicationListen
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
|
@SuppressWarnings("unchecked")
|
||||||
Class<?> declaredEventType = resolveDeclaredEventType(this.delegate.getClass());
|
public boolean supportsEventType(ResolvableType eventType) {
|
||||||
if (declaredEventType == null || declaredEventType.equals(ApplicationEvent.class)) {
|
if (this.delegate instanceof SmartApplicationListener) {
|
||||||
Class<?> targetClass = AopUtils.getTargetClass(this.delegate);
|
Class<? extends ApplicationEvent> eventClass = (Class<? extends ApplicationEvent>) eventType.getRawClass();
|
||||||
if (targetClass != this.delegate.getClass()) {
|
return ((SmartApplicationListener) this.delegate).supportsEventType(eventClass);
|
||||||
declaredEventType = resolveDeclaredEventType(targetClass);
|
}
|
||||||
}
|
else {
|
||||||
|
return (this.declaredEventType == null || this.declaredEventType.isAssignableFrom(eventType));
|
||||||
}
|
}
|
||||||
return (declaredEventType == null || declaredEventType.isAssignableFrom(eventType));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsSourceType(Class<?> sourceType) {
|
public boolean supportsSourceType(Class<?> sourceType) {
|
||||||
return true;
|
if (this.delegate instanceof SmartApplicationListener) {
|
||||||
|
return ((SmartApplicationListener) this.delegate).supportsSourceType(sourceType);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -74,9 +82,25 @@ public class GenericApplicationListenerAdapter implements SmartApplicationListen
|
||||||
return (this.delegate instanceof Ordered ? ((Ordered) this.delegate).getOrder() : Ordered.LOWEST_PRECEDENCE);
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
static Class<?> resolveDeclaredEventType(Class<?> listenerType) {
|
private static ResolvableType resolveDeclaredEventType(ApplicationListener<ApplicationEvent> listener) {
|
||||||
return GenericTypeResolver.resolveTypeArgument(listenerType, ApplicationListener.class);
|
ResolvableType declaredEventType = resolveDeclaredEventType(listener.getClass());
|
||||||
|
if (declaredEventType == null || declaredEventType.isAssignableFrom(
|
||||||
|
ResolvableType.forClass(ApplicationEvent.class))) {
|
||||||
|
|
||||||
|
Class<?> targetClass = AopUtils.getTargetClass(listener);
|
||||||
|
if (targetClass != listener.getClass()) {
|
||||||
|
declaredEventType = resolveDeclaredEventType(targetClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return declaredEventType;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -21,6 +21,7 @@ import java.util.concurrent.Executor;
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
import org.springframework.beans.factory.BeanFactory;
|
||||||
import org.springframework.context.ApplicationEvent;
|
import org.springframework.context.ApplicationEvent;
|
||||||
import org.springframework.context.ApplicationListener;
|
import org.springframework.context.ApplicationListener;
|
||||||
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.util.ErrorHandler;
|
import org.springframework.util.ErrorHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -38,6 +39,7 @@ import org.springframework.util.ErrorHandler;
|
||||||
*
|
*
|
||||||
* @author Rod Johnson
|
* @author Rod Johnson
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
|
* @author Stephane Nicoll
|
||||||
* @see #setTaskExecutor
|
* @see #setTaskExecutor
|
||||||
*/
|
*/
|
||||||
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
|
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
|
||||||
|
|
@ -113,8 +115,14 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void multicastEvent(final ApplicationEvent event) {
|
public void multicastEvent(ApplicationEvent event) {
|
||||||
for (final ApplicationListener<?> listener : getApplicationListeners(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();
|
Executor executor = getTaskExecutor();
|
||||||
if (executor != null) {
|
if (executor != null) {
|
||||||
executor.execute(new Runnable() {
|
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.
|
* Invoke the given listener with the given event.
|
||||||
* @param listener the ApplicationListener to invoke
|
* @param listener the ApplicationListener to invoke
|
||||||
|
|
|
||||||
|
|
@ -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");
|
* 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.
|
||||||
|
|
@ -24,8 +24,13 @@ import org.springframework.core.Ordered;
|
||||||
* Extended variant of the standard {@link ApplicationListener} interface,
|
* Extended variant of the standard {@link ApplicationListener} interface,
|
||||||
* exposing further metadata such as the supported event type.
|
* exposing further metadata such as the supported event type.
|
||||||
*
|
*
|
||||||
|
* <p>Users are <bold>strongly advised</bold> to use the {@link GenericApplicationListener}
|
||||||
|
* interface instead as it provides an improved detection of generics-based
|
||||||
|
* event types.
|
||||||
|
*
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
|
* @see GenericApplicationListener
|
||||||
*/
|
*/
|
||||||
public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
|
public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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");
|
* 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.
|
||||||
|
|
@ -19,6 +19,7 @@ package org.springframework.context.event;
|
||||||
import org.springframework.context.ApplicationEvent;
|
import org.springframework.context.ApplicationEvent;
|
||||||
import org.springframework.context.ApplicationListener;
|
import org.springframework.context.ApplicationListener;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.core.ResolvableType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link org.springframework.context.ApplicationListener} decorator that filters
|
* {@link org.springframework.context.ApplicationListener} decorator that filters
|
||||||
|
|
@ -29,13 +30,14 @@ import org.springframework.core.Ordered;
|
||||||
* method instead of specifying a delegate listener.
|
* method instead of specifying a delegate listener.
|
||||||
*
|
*
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
|
* @author Stephane Nicoll
|
||||||
* @since 2.0.5
|
* @since 2.0.5
|
||||||
*/
|
*/
|
||||||
public class SourceFilteringListener implements SmartApplicationListener {
|
public class SourceFilteringListener implements GenericApplicationListener {
|
||||||
|
|
||||||
private final Object source;
|
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) {
|
public SourceFilteringListener(Object source, ApplicationListener<?> delegate) {
|
||||||
this.source = source;
|
this.source = source;
|
||||||
this.delegate = (delegate instanceof SmartApplicationListener ?
|
this.delegate = (delegate instanceof GenericApplicationListener ?
|
||||||
(SmartApplicationListener) delegate : new GenericApplicationListenerAdapter(delegate));
|
(GenericApplicationListener) delegate : new GenericApplicationListenerAdapter(delegate));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -71,7 +73,7 @@ public class SourceFilteringListener implements SmartApplicationListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
|
public boolean supportsEventType(ResolvableType eventType) {
|
||||||
return (this.delegate == null || this.delegate.supportsEventType(eventType));
|
return (this.delegate == null || this.delegate.supportsEventType(eventType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -63,6 +63,7 @@ import org.springframework.context.event.SimpleApplicationEventMulticaster;
|
||||||
import org.springframework.context.expression.StandardBeanExpressionResolver;
|
import org.springframework.context.expression.StandardBeanExpressionResolver;
|
||||||
import org.springframework.context.weaving.LoadTimeWeaverAware;
|
import org.springframework.context.weaving.LoadTimeWeaverAware;
|
||||||
import org.springframework.context.weaving.LoadTimeWeaverAwareProcessor;
|
import org.springframework.context.weaving.LoadTimeWeaverAwareProcessor;
|
||||||
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.core.convert.ConversionService;
|
import org.springframework.core.convert.ConversionService;
|
||||||
import org.springframework.core.env.ConfigurableEnvironment;
|
import org.springframework.core.env.ConfigurableEnvironment;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
|
|
@ -107,6 +108,7 @@ import org.springframework.util.ObjectUtils;
|
||||||
* @author Rod Johnson
|
* @author Rod Johnson
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
|
* @author Stephane Nicoll
|
||||||
* @since January 21, 2001
|
* @since January 21, 2001
|
||||||
* @see #refreshBeanFactory
|
* @see #refreshBeanFactory
|
||||||
* @see #getBeanFactory
|
* @see #getBeanFactory
|
||||||
|
|
@ -324,13 +326,22 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void publishEvent(ApplicationEvent event) {
|
public void publishEvent(ApplicationEvent event) {
|
||||||
|
publishEvent(event, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void publishEvent(ApplicationEvent event, ResolvableType eventType) {
|
||||||
Assert.notNull(event, "Event must not be null");
|
Assert.notNull(event, "Event must not be null");
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Publishing event in " + getDisplayName() + ": " + event);
|
logger.trace("Publishing event in " + getDisplayName() + ": " + event);
|
||||||
}
|
}
|
||||||
getApplicationEventMulticaster().multicastEvent(event);
|
getApplicationEventMulticaster().multicastEvent(event, eventType);
|
||||||
if (this.parent != null) {
|
if (this.parent != null) {
|
||||||
this.parent.publishEvent(event);
|
if (this.parent instanceof AbstractApplicationContext) {
|
||||||
|
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.parent.publishEvent(event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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<T>
|
||||||
|
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<String> {
|
||||||
|
|
||||||
|
public StringEvent(Object source, String payload) {
|
||||||
|
super(source, payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static class LongEvent extends GenericApplicationEvent<Long> {
|
||||||
|
|
||||||
|
public LongEvent(Object source, Long payload) {
|
||||||
|
super(source, payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected <T> GenericApplicationEvent<T> createGenericEvent(T payload) {
|
||||||
|
return new GenericApplicationEvent<>(this, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static class GenericEventListener implements ApplicationListener<GenericApplicationEvent<?>> {
|
||||||
|
@Override
|
||||||
|
public void onApplicationEvent(GenericApplicationEvent<?> event) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ObjectEventListener implements ApplicationListener<GenericApplicationEvent<Object>> {
|
||||||
|
@Override
|
||||||
|
public void onApplicationEvent(GenericApplicationEvent<Object> event) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class UpperBoundEventListener
|
||||||
|
implements ApplicationListener<GenericApplicationEvent<? extends RuntimeException>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onApplicationEvent(GenericApplicationEvent<? extends RuntimeException> event) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class StringEventListener implements ApplicationListener<GenericApplicationEvent<String>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onApplicationEvent(GenericApplicationEvent<String> event) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class RawApplicationListener implements ApplicationListener {
|
||||||
|
@Override
|
||||||
|
public void onApplicationEvent(ApplicationEvent event) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
static class GenericApplicationEvents {
|
||||||
|
|
||||||
|
public GenericApplicationEvent<?> wildcardEvent;
|
||||||
|
|
||||||
|
public GenericApplicationEvent<String> stringEvent;
|
||||||
|
|
||||||
|
public GenericApplicationEvent<Long> longEvent;
|
||||||
|
|
||||||
|
public GenericApplicationEvent<IllegalStateException> illegalStateExceptionEvent;
|
||||||
|
|
||||||
|
public GenericApplicationEvent<IOException> ioExceptionEvent;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -34,6 +34,7 @@ import org.springframework.context.BeanThatListens;
|
||||||
import org.springframework.context.support.AbstractApplicationContext;
|
import org.springframework.context.support.AbstractApplicationContext;
|
||||||
import org.springframework.context.support.StaticApplicationContext;
|
import org.springframework.context.support.StaticApplicationContext;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.scheduling.support.TaskUtils;
|
import org.springframework.scheduling.support.TaskUtils;
|
||||||
import org.springframework.tests.sample.beans.TestBean;
|
import org.springframework.tests.sample.beans.TestBean;
|
||||||
|
|
||||||
|
|
@ -45,20 +46,59 @@ import static org.mockito.BDDMockito.*;
|
||||||
*
|
*
|
||||||
* @author Alef Arendsen
|
* @author Alef Arendsen
|
||||||
* @author Rick Evans
|
* @author Rick Evans
|
||||||
|
* @author Stephane Nicoll
|
||||||
*/
|
*/
|
||||||
public class ApplicationContextEventTests {
|
public class ApplicationContextEventTests extends AbstractApplicationEventListenerTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void simpleApplicationEventMulticaster() {
|
public void multicastSimpleEvent() {
|
||||||
@SuppressWarnings("unchecked")
|
multicastEvent(true, ApplicationListener.class,
|
||||||
ApplicationListener<ApplicationEvent> listener = mock(ApplicationListener.class);
|
new ContextClosedEvent(new StaticApplicationContext()), null);
|
||||||
ApplicationEvent evt = new ContextClosedEvent(new StaticApplicationContext());
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multicastGenericEvent() {
|
||||||
|
multicastEvent(true, StringEventListener.class, createGenericEvent("test"),
|
||||||
|
getGenericApplicationEventType("stringEvent"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multicastGenericEventWrongType() {
|
||||||
|
multicastEvent(false, StringEventListener.class, createGenericEvent(123L),
|
||||||
|
getGenericApplicationEventType("longEvent"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // Unfortunate - this should work as well
|
||||||
|
public void multicastGenericEventWildcardSubType() {
|
||||||
|
multicastEvent(false, StringEventListener.class, createGenericEvent("test"),
|
||||||
|
getGenericApplicationEventType("wildcardEvent"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multicastConcreteTypeGenericListener() {
|
||||||
|
multicastEvent(true, StringEventListener.class, new StringEvent(this, "test"), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multicastConcreteWrongTypeGenericListener() {
|
||||||
|
multicastEvent(false, StringEventListener.class, new LongEvent(this, 123L), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void multicastEvent(boolean match, Class<?> listenerType,
|
||||||
|
ApplicationEvent event, ResolvableType eventType) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
ApplicationListener<ApplicationEvent> listener =
|
||||||
|
(ApplicationListener<ApplicationEvent>) mock(listenerType);
|
||||||
SimpleApplicationEventMulticaster smc = new SimpleApplicationEventMulticaster();
|
SimpleApplicationEventMulticaster smc = new SimpleApplicationEventMulticaster();
|
||||||
smc.addApplicationListener(listener);
|
smc.addApplicationListener(listener);
|
||||||
|
|
||||||
smc.multicastEvent(evt);
|
if (eventType != null) {
|
||||||
verify(listener).onApplicationEvent(evt);
|
smc.multicastEvent(event, eventType);
|
||||||
|
} else {
|
||||||
|
smc.multicastEvent(event);
|
||||||
|
}
|
||||||
|
int invocation = match ? 1 : 0;
|
||||||
|
verify(listener, times(invocation)).onApplicationEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,148 @@
|
||||||
|
/*
|
||||||
|
* 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.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.context.ApplicationEvent;
|
||||||
|
import org.springframework.context.ApplicationListener;
|
||||||
|
import org.springframework.core.ResolvableType;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
public class GenericApplicationListenerAdapterTests extends AbstractApplicationEventListenerTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void supportsEventTypeWithSmartApplicationListener() {
|
||||||
|
SmartApplicationListener smartListener = mock(SmartApplicationListener.class);
|
||||||
|
GenericApplicationListenerAdapter listener = new GenericApplicationListenerAdapter(smartListener);
|
||||||
|
ResolvableType type = ResolvableType.forClass(ApplicationEvent.class);
|
||||||
|
listener.supportsEventType(type);
|
||||||
|
verify(smartListener, times(1)).supportsEventType(ApplicationEvent.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void supportsSourceTypeWithSmartApplicationListener() {
|
||||||
|
SmartApplicationListener smartListener = mock(SmartApplicationListener.class);
|
||||||
|
GenericApplicationListenerAdapter listener = new GenericApplicationListenerAdapter(smartListener);
|
||||||
|
listener.supportsSourceType(Object.class);
|
||||||
|
verify(smartListener, times(1)).supportsSourceType(Object.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void genericListenerStrictType() {
|
||||||
|
supportsEventType(true, StringEventListener.class, getGenericApplicationEventType("stringEvent"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // Demonstrates we can't inject that event because the generic type is lost
|
||||||
|
public void genericListenerStrictTypeTypeErasure() {
|
||||||
|
GenericApplicationEvent<String> stringEvent = createGenericEvent("test");
|
||||||
|
ResolvableType eventType = ResolvableType.forType(stringEvent.getClass());
|
||||||
|
supportsEventType(false, StringEventListener.class, eventType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // But it works if we specify the type properly
|
||||||
|
public void genericListenerStrictTypeAndResolvableType() {
|
||||||
|
ResolvableType eventType = ResolvableType
|
||||||
|
.forClassWithGenerics(GenericApplicationEvent.class, String.class);
|
||||||
|
supportsEventType(true, StringEventListener.class, eventType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // Demonstrates it works if we actually use the subtype
|
||||||
|
public void genericListenerStrictTypeEventSubType() {
|
||||||
|
StringEvent stringEvent = new StringEvent(this, "test");
|
||||||
|
ResolvableType eventType = ResolvableType.forType(stringEvent.getClass());
|
||||||
|
supportsEventType(true, StringEventListener.class, eventType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void genericListenerStrictTypeNotMatching() {
|
||||||
|
supportsEventType(false, StringEventListener.class, getGenericApplicationEventType("longEvent"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void genericListenerStrictTypeEventSubTypeNotMatching() {
|
||||||
|
LongEvent stringEvent = new LongEvent(this, 123L);
|
||||||
|
ResolvableType eventType = ResolvableType.forType(stringEvent.getClass());
|
||||||
|
supportsEventType(false, StringEventListener.class, eventType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void genericListenerStrictTypeNotMatchTypeErasure() {
|
||||||
|
GenericApplicationEvent<Long> longEvent = createGenericEvent(123L);
|
||||||
|
ResolvableType eventType = ResolvableType.forType(longEvent.getClass());
|
||||||
|
supportsEventType(false, StringEventListener.class, eventType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void genericListenerStrictTypeSubClass() {
|
||||||
|
supportsEventType(false, ObjectEventListener.class,
|
||||||
|
getGenericApplicationEventType("longEvent"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void genericListenerUpperBoundType() {
|
||||||
|
supportsEventType(true, UpperBoundEventListener.class,
|
||||||
|
getGenericApplicationEventType("illegalStateExceptionEvent"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void genericListenerUpperBoundTypeNotMatching() throws NoSuchFieldException {
|
||||||
|
supportsEventType(false, UpperBoundEventListener.class,
|
||||||
|
getGenericApplicationEventType("ioExceptionEvent"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void genericListenerWildcardType() {
|
||||||
|
supportsEventType(true, GenericEventListener.class,
|
||||||
|
getGenericApplicationEventType("stringEvent"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // Demonstrates we cant inject that event because the listener has a wildcard
|
||||||
|
public void genericListenerWildcardTypeTypeErasure() {
|
||||||
|
GenericApplicationEvent<String> stringEvent = createGenericEvent("test");
|
||||||
|
ResolvableType eventType = ResolvableType.forType(stringEvent.getClass());
|
||||||
|
supportsEventType(true, GenericEventListener.class, eventType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void genericListenerRawType() {
|
||||||
|
supportsEventType(true, RawApplicationListener.class,
|
||||||
|
getGenericApplicationEventType("stringEvent"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // Demonstrates we cant inject that event because the listener has a raw type
|
||||||
|
public void genericListenerRawTypeTypeErasure() {
|
||||||
|
GenericApplicationEvent<String> stringEvent = createGenericEvent("test");
|
||||||
|
ResolvableType eventType = ResolvableType.forType(stringEvent.getClass());
|
||||||
|
supportsEventType(true, RawApplicationListener.class, eventType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void supportsEventType(boolean match, Class<? extends ApplicationListener> listenerType,
|
||||||
|
ResolvableType eventType) {
|
||||||
|
|
||||||
|
ApplicationListener<?> listener = mock(listenerType);
|
||||||
|
GenericApplicationListenerAdapter adapter = new GenericApplicationListenerAdapter(listener);
|
||||||
|
assertEquals("Wrong match for event '" + eventType + "' on " + listenerType.getClass().getName(),
|
||||||
|
match, adapter.supportsEventType(eventType));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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");
|
* 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.
|
||||||
|
|
@ -28,6 +28,7 @@ import org.springframework.context.ApplicationEvent;
|
||||||
import org.springframework.context.BeanThatListens;
|
import org.springframework.context.BeanThatListens;
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
import org.springframework.context.event.SimpleApplicationEventMulticaster;
|
import org.springframework.context.event.SimpleApplicationEventMulticaster;
|
||||||
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.core.io.support.EncodedResource;
|
import org.springframework.core.io.support.EncodedResource;
|
||||||
|
|
@ -92,8 +93,8 @@ public class StaticApplicationContextMulticasterTests extends AbstractApplicatio
|
||||||
private static int counter = 0;
|
private static int counter = 0;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void multicastEvent(ApplicationEvent event) {
|
public void multicastEvent(ApplicationEvent event, ResolvableType eventType) {
|
||||||
super.multicastEvent(event);
|
super.multicastEvent(event, eventType);
|
||||||
counter++;
|
counter++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue