SmartApplicationListener interface supports source type checking; SimpleApplicationEventMulticaster caches information about event/source matches
git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@646 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
parent
14847cc662
commit
6ad7cabd2c
|
|
@ -17,13 +17,15 @@
|
|||
package org.springframework.context.event;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.OrderComparator;
|
||||
|
||||
|
|
@ -50,44 +52,167 @@ import org.springframework.core.OrderComparator;
|
|||
public abstract class AbstractApplicationEventMulticaster
|
||||
implements ApplicationEventMulticaster, BeanFactoryAware {
|
||||
|
||||
private final Set<ApplicationListener> applicationListeners = new LinkedHashSet<ApplicationListener>();
|
||||
private final ListenerRetriever defaultRetriever = new ListenerRetriever();
|
||||
|
||||
private final Set<String> applicationListenerBeans = new LinkedHashSet<String>();
|
||||
private final Map<ListenerCacheKey, ListenerRetriever> retrieverCache =
|
||||
new ConcurrentHashMap<ListenerCacheKey, ListenerRetriever>();
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
|
||||
public void addApplicationListener(ApplicationListener listener) {
|
||||
this.applicationListeners.add(listener);
|
||||
this.defaultRetriever.applicationListeners.add(listener);
|
||||
}
|
||||
|
||||
public void addApplicationListenerBean(String listenerBeanName) {
|
||||
this.applicationListenerBeans.add(listenerBeanName);
|
||||
this.defaultRetriever.applicationListenerBeans.add(listenerBeanName);
|
||||
}
|
||||
|
||||
public final void setBeanFactory(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
private BeanFactory getBeanFactory() {
|
||||
if (this.beanFactory == null) {
|
||||
throw new IllegalStateException("ApplicationEventMulticaster cannot retrieve listener beans " +
|
||||
"because it is not associated with a BeanFactory");
|
||||
}
|
||||
return this.beanFactory;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the current Collection of ApplicationListeners.
|
||||
* Return a Collection containing all ApplicationListeners.
|
||||
* @return a Collection of ApplicationListeners
|
||||
* @see org.springframework.context.ApplicationListener
|
||||
*/
|
||||
protected Collection<ApplicationListener> getApplicationListeners() {
|
||||
LinkedList<ApplicationListener> allListeners =
|
||||
new LinkedList<ApplicationListener>(this.applicationListeners);
|
||||
if (!this.applicationListenerBeans.isEmpty()) {
|
||||
if (this.beanFactory == null) {
|
||||
throw new IllegalStateException("ApplicationEventMulticaster cannot retrieve listener beans " +
|
||||
"because it is not associated with a BeanFactory: " + this.applicationListenerBeans);
|
||||
}
|
||||
for (String listenerBeanName : applicationListenerBeans) {
|
||||
allListeners.add(this.beanFactory.getBean(listenerBeanName, ApplicationListener.class));
|
||||
}
|
||||
return this.defaultRetriever.getApplicationListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Collection of ApplicationListeners matching the given
|
||||
* 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.
|
||||
* @return a Collection of ApplicationListeners
|
||||
* @see org.springframework.context.ApplicationListener
|
||||
*/
|
||||
protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event) {
|
||||
Class<? extends ApplicationEvent> eventType = event.getClass();
|
||||
Class sourceType = event.getSource().getClass();
|
||||
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
|
||||
ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
|
||||
if (retriever != null) {
|
||||
return retriever.getApplicationListeners();
|
||||
}
|
||||
else {
|
||||
retriever = new ListenerRetriever();
|
||||
LinkedList<ApplicationListener> allListeners = new LinkedList<ApplicationListener>();
|
||||
for (ApplicationListener listener : this.defaultRetriever.applicationListeners) {
|
||||
if (supportsEvent(listener, eventType, sourceType)) {
|
||||
retriever.applicationListeners.add(listener);
|
||||
allListeners.add(listener);
|
||||
}
|
||||
}
|
||||
if (!this.defaultRetriever.applicationListenerBeans.isEmpty()) {
|
||||
BeanFactory beanFactory = getBeanFactory();
|
||||
for (String listenerBeanName : this.defaultRetriever.applicationListenerBeans) {
|
||||
ApplicationListener listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
|
||||
if (supportsEvent(listener, eventType, sourceType)) {
|
||||
retriever.applicationListenerBeans.add(listenerBeanName);
|
||||
allListeners.add(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
OrderComparator.sort(allListeners);
|
||||
this.retrieverCache.put(cacheKey, retriever);
|
||||
return allListeners;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given listener supports the given event.
|
||||
* <p>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.
|
||||
* @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
|
||||
*/
|
||||
protected boolean supportsEvent(
|
||||
ApplicationListener listener, Class<? extends ApplicationEvent> eventType, Class sourceType) {
|
||||
|
||||
SmartApplicationListener smartListener = (listener instanceof SmartApplicationListener ?
|
||||
(SmartApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
|
||||
return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cache key for ListenerRetrievers, based on event type and source type.
|
||||
*/
|
||||
private static class ListenerCacheKey {
|
||||
|
||||
private final Class eventType;
|
||||
|
||||
private final Class sourceType;
|
||||
|
||||
public ListenerCacheKey(Class eventType, Class sourceType) {
|
||||
this.eventType = eventType;
|
||||
this.sourceType = sourceType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
ListenerCacheKey otherKey = (ListenerCacheKey) other;
|
||||
return (this.eventType.equals(otherKey.eventType) && this.sourceType.equals(otherKey.sourceType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.eventType.hashCode() * 29 + this.sourceType.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper class that encapsulates a specific set of target listeners,
|
||||
* allowing for efficient retrieval of pre-filtered listeners.
|
||||
* <p>An instance of this helper gets cached per event type and source type.
|
||||
*/
|
||||
private class ListenerRetriever {
|
||||
|
||||
public final Set<ApplicationListener> applicationListeners;
|
||||
|
||||
public final Set<String> applicationListenerBeans;
|
||||
|
||||
public ListenerRetriever() {
|
||||
this.applicationListeners = new LinkedHashSet<ApplicationListener>();
|
||||
this.applicationListenerBeans = new LinkedHashSet<String>();
|
||||
}
|
||||
|
||||
public Collection<ApplicationListener> getApplicationListeners() {
|
||||
LinkedList<ApplicationListener> allListeners = new LinkedList<ApplicationListener>();
|
||||
for (ApplicationListener listener : this.applicationListeners) {
|
||||
allListeners.add(listener);
|
||||
}
|
||||
if (!this.applicationListenerBeans.isEmpty()) {
|
||||
BeanFactory beanFactory = getBeanFactory();
|
||||
for (String listenerBeanName : this.applicationListenerBeans) {
|
||||
ApplicationListener listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
|
||||
allListeners.add(listener);
|
||||
}
|
||||
}
|
||||
OrderComparator.sort(allListeners);
|
||||
return allListeners;
|
||||
}
|
||||
Collections.sort(allListeners, new OrderComparator());
|
||||
return allListeners;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,10 @@ public class GenericApplicationListenerAdapter implements SmartApplicationListen
|
|||
return getGenericEventType(this.delegate.getClass()).isAssignableFrom(eventType);
|
||||
}
|
||||
|
||||
public boolean supportsSourceType(Class<?> sourceType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return (this.delegate instanceof Ordered ? ((Ordered) this.delegate).getOrder() : Ordered.LOWEST_PRECEDENCE);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ 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.task.SyncTaskExecutor;
|
||||
|
||||
/**
|
||||
* Simple implementation of the {@link ApplicationEventMulticaster} interface.
|
||||
|
|
@ -42,7 +41,7 @@ import org.springframework.core.task.SyncTaskExecutor;
|
|||
*/
|
||||
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
|
||||
|
||||
private Executor taskExecutor = new SyncTaskExecutor();
|
||||
private Executor taskExecutor;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -72,7 +71,7 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM
|
|||
* @see org.springframework.scheduling.timer.TimerTaskExecutor
|
||||
*/
|
||||
public void setTaskExecutor(Executor taskExecutor) {
|
||||
this.taskExecutor = (taskExecutor != null ? taskExecutor : new SyncTaskExecutor());
|
||||
this.taskExecutor = taskExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -83,18 +82,21 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM
|
|||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void multicastEvent(final ApplicationEvent event) {
|
||||
for (final ApplicationListener listener : getApplicationListeners()) {
|
||||
SmartApplicationListener smartListener = (listener instanceof SmartApplicationListener ?
|
||||
(SmartApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
|
||||
if (smartListener.supportsEventType(event.getClass())) {
|
||||
getTaskExecutor().execute(new Runnable() {
|
||||
for (final ApplicationListener listener : getApplicationListeners(event)) {
|
||||
Executor executor = getTaskExecutor();
|
||||
if (executor != null) {
|
||||
executor.execute(new Runnable() {
|
||||
@SuppressWarnings("unchecked")
|
||||
public void run() {
|
||||
listener.onApplicationEvent(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
listener.onApplicationEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,4 +34,9 @@ public interface SmartApplicationListener extends ApplicationListener, Ordered {
|
|||
*/
|
||||
boolean supportsEventType(Class<? extends ApplicationEvent> eventType);
|
||||
|
||||
/**
|
||||
* Determine whether this listener actually supports the given source type.
|
||||
*/
|
||||
boolean supportsSourceType(Class<?> sourceType);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,6 +73,10 @@ public class SourceFilteringListener implements SmartApplicationListener {
|
|||
return (this.delegate == null || this.delegate.supportsEventType(eventType));
|
||||
}
|
||||
|
||||
public boolean supportsSourceType(Class<?> sourceType) {
|
||||
return sourceType.isInstance(this.source);
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return (this.delegate != null ? this.delegate.getOrder() : Ordered.LOWEST_PRECEDENCE);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,8 +22,7 @@ import java.util.Set;
|
|||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.easymock.EasyMock;
|
||||
import static org.easymock.EasyMock.*;
|
||||
import org.junit.Assert;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
|
|
@ -89,6 +88,30 @@ public class ApplicationContextEventTests {
|
|||
verify(invocation, ctx);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listenersInApplicationContext() {
|
||||
StaticApplicationContext context = new StaticApplicationContext();
|
||||
context.registerBeanDefinition("listener1", new RootBeanDefinition(MyOrderedListener1.class));
|
||||
RootBeanDefinition listener2 = new RootBeanDefinition(MyOrderedListener2.class);
|
||||
listener2.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference("listener1"));
|
||||
context.registerBeanDefinition("listener2", listener2);
|
||||
context.refresh();
|
||||
|
||||
MyOrderedListener1 listener1 = context.getBean("listener1", MyOrderedListener1.class);
|
||||
MyEvent event1 = new MyEvent(context);
|
||||
context.publishEvent(event1);
|
||||
MyOtherEvent event2 = new MyOtherEvent(context);
|
||||
context.publishEvent(event2);
|
||||
MyEvent event3 = new MyEvent(context);
|
||||
context.publishEvent(event3);
|
||||
MyOtherEvent event4 = new MyOtherEvent(context);
|
||||
context.publishEvent(event4);
|
||||
assertTrue(listener1.seenEvents.contains(event1));
|
||||
assertTrue(listener1.seenEvents.contains(event2));
|
||||
assertTrue(listener1.seenEvents.contains(event3));
|
||||
assertTrue(listener1.seenEvents.contains(event4));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listenerAndBroadcasterWithCircularReference() {
|
||||
StaticApplicationContext context = new StaticApplicationContext();
|
||||
|
|
@ -98,9 +121,9 @@ public class ApplicationContextEventTests {
|
|||
context.registerBeanDefinition("listener", listenerDef);
|
||||
context.refresh();
|
||||
|
||||
BeanThatBroadcasts broadcaster = (BeanThatBroadcasts) context.getBean("broadcaster");
|
||||
BeanThatBroadcasts broadcaster = context.getBean("broadcaster", BeanThatBroadcasts.class);
|
||||
context.publishEvent(new MyEvent(context));
|
||||
Assert.assertEquals("The event was not received by the listener", 2, broadcaster.receivedCount);
|
||||
assertEquals("The event was not received by the listener", 2, broadcaster.receivedCount);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* Copyright 2002-2009 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.
|
||||
|
|
@ -16,7 +16,10 @@
|
|||
|
||||
package org.springframework.core;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@link Comparator} implementation for {@link Ordered} objects,
|
||||
|
|
@ -34,6 +37,12 @@ import java.util.Comparator;
|
|||
*/
|
||||
public class OrderComparator implements Comparator<Object> {
|
||||
|
||||
/**
|
||||
* Shared default instance of OrderComparator.
|
||||
*/
|
||||
public static OrderComparator INSTANCE = new OrderComparator();
|
||||
|
||||
|
||||
public int compare(Object o1, Object o2) {
|
||||
boolean p1 = (o1 instanceof PriorityOrdered);
|
||||
boolean p2 = (o2 instanceof PriorityOrdered);
|
||||
|
|
@ -61,4 +70,31 @@ public class OrderComparator implements Comparator<Object> {
|
|||
return (obj instanceof Ordered ? ((Ordered) obj).getOrder() : Ordered.LOWEST_PRECEDENCE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sort the given List with a default OrderComparator.
|
||||
* <p>Optimized to skip sorting for lists with size 0 or 1,
|
||||
* in order to avoid unnecessary array extraction.
|
||||
* @param list the List to sort
|
||||
* @see java.util.Collections#sort(java.util.List, java.util.Comparator)
|
||||
*/
|
||||
public static void sort(List<?> list) {
|
||||
if (list.size() > 1) {
|
||||
Collections.sort(list, INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the given array with a default OrderComparator.
|
||||
* <p>Optimized to skip sorting for lists with size 0 or 1,
|
||||
* in order to avoid unnecessary array extraction.
|
||||
* @param array the array to sort
|
||||
* @see java.util.Arrays#sort(Object[], java.util.Comparator)
|
||||
*/
|
||||
public static void sort(Object[] array) {
|
||||
if (array.length > 1) {
|
||||
Arrays.sort(array, INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue