ApplicationListener beans get obtained on demand, supporting non-singletons as well; ApplicationListeners will be called in the order according to the Ordered contract; generified ApplicationListener interface
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2006 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.
|
||||
@@ -24,14 +24,15 @@ import java.util.EventListener;
|
||||
* for the Observer design pattern.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @see org.springframework.context.event.ApplicationEventMulticaster
|
||||
*/
|
||||
public interface ApplicationListener extends EventListener {
|
||||
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
|
||||
|
||||
/**
|
||||
* Handle an application event.
|
||||
* @param event the event to respond to
|
||||
*/
|
||||
void onApplicationEvent(ApplicationEvent event);
|
||||
void onApplicationEvent(E event);
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -17,11 +17,15 @@
|
||||
package org.springframework.context.event;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.OrderComparator;
|
||||
|
||||
/**
|
||||
* Abstract implementation of the {@link ApplicationEventMulticaster} interface,
|
||||
@@ -43,73 +47,47 @@ import org.springframework.context.ApplicationListener;
|
||||
* @see #getApplicationListeners()
|
||||
* @see SimpleApplicationEventMulticaster
|
||||
*/
|
||||
public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster {
|
||||
public abstract class AbstractApplicationEventMulticaster
|
||||
implements ApplicationEventMulticaster, BeanFactoryAware {
|
||||
|
||||
/** Collection of ApplicationListeners */
|
||||
private Collection<ApplicationListener> applicationListeners = new LinkedHashSet<ApplicationListener>();
|
||||
private final Set<ApplicationListener> applicationListeners = new LinkedHashSet<ApplicationListener>();
|
||||
|
||||
private final Set<String> applicationListenerBeans = new LinkedHashSet<String>();
|
||||
|
||||
/**
|
||||
* Set whether this multicaster should expect concurrent updates at runtime
|
||||
* (i.e. after context startup finished). In case of concurrent updates,
|
||||
* a copy-on-write strategy is applied, keeping iteration (for multicasting)
|
||||
* without synchronization while still making listener updates thread-safe.
|
||||
*/
|
||||
public void setConcurrentUpdates(boolean concurrent) {
|
||||
Collection<ApplicationListener> newColl = concurrent ?
|
||||
new CopyOnWriteArraySet<ApplicationListener>() : new LinkedHashSet<ApplicationListener>();
|
||||
// Add all previously registered listeners (usually none).
|
||||
newColl.addAll(this.applicationListeners);
|
||||
this.applicationListeners = newColl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the collection class to use. Can be populated with a fully
|
||||
* qualified class name when defined in a Spring application context.
|
||||
* <p>Default is a linked HashSet, keeping the registration order.
|
||||
* Note that a Set class specified will not permit multiple instances
|
||||
* of the same listener, while a List class will allow for registering
|
||||
* the same listener multiple times.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setCollectionClass(Class collectionClass) {
|
||||
if (collectionClass == null) {
|
||||
throw new IllegalArgumentException("'collectionClass' must not be null");
|
||||
}
|
||||
if (!Collection.class.isAssignableFrom(collectionClass)) {
|
||||
throw new IllegalArgumentException("'collectionClass' must implement [java.util.Collection]");
|
||||
}
|
||||
// Create desired collection instance.
|
||||
Collection<ApplicationListener> newColl =
|
||||
(Collection<ApplicationListener>) BeanUtils.instantiateClass(collectionClass);
|
||||
// Add all previously registered listeners (usually none).
|
||||
newColl.addAll(this.applicationListeners);
|
||||
this.applicationListeners = newColl;
|
||||
}
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
|
||||
public void addApplicationListener(ApplicationListener listener) {
|
||||
this.applicationListeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeApplicationListener(ApplicationListener listener) {
|
||||
this.applicationListeners.remove(listener);
|
||||
public void addApplicationListenerBean(String listenerBeanName) {
|
||||
this.applicationListenerBeans.add(listenerBeanName);
|
||||
}
|
||||
|
||||
public void removeAllListeners() {
|
||||
this.applicationListeners.clear();
|
||||
public final void setBeanFactory(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current Collection of ApplicationListeners.
|
||||
* <p>Note that this is the raw Collection of ApplicationListeners,
|
||||
* potentially modified when new listeners get registered or
|
||||
* existing ones get removed. This Collection is not a snapshot copy.
|
||||
* @return a Collection of ApplicationListeners
|
||||
* @see org.springframework.context.ApplicationListener
|
||||
*/
|
||||
protected Collection<ApplicationListener> getApplicationListeners() {
|
||||
return this.applicationListeners;
|
||||
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));
|
||||
}
|
||||
}
|
||||
Collections.sort(allListeners, new OrderComparator());
|
||||
return allListeners;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2002-2005 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.
|
||||
* 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.
|
||||
@@ -27,6 +27,7 @@ import org.springframework.context.ApplicationListener;
|
||||
* as a helper to publish events to listeners.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public interface ApplicationEventMulticaster {
|
||||
|
||||
@@ -37,17 +38,10 @@ public interface ApplicationEventMulticaster {
|
||||
void addApplicationListener(ApplicationListener listener);
|
||||
|
||||
/**
|
||||
* Remove a listener from the notification list.
|
||||
* @param listener the listener to remove
|
||||
* Add a listener to be notified of all events.
|
||||
* @param listenerBeanName the name of the listener bean to add
|
||||
*/
|
||||
void removeApplicationListener(ApplicationListener listener);
|
||||
|
||||
/**
|
||||
* Remove all listeners registered with this multicaster.
|
||||
* It will perform no action on event notification until more
|
||||
* listeners are registered.
|
||||
*/
|
||||
void removeAllListeners();
|
||||
void addApplicationListenerBean(String listenerBeanName);
|
||||
|
||||
/**
|
||||
* Multicast the given application event to appropriate listeners.
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link SmartApplicationListener} adapter that determines supported event types
|
||||
* through introspecting the generically declared type of the target listener.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
* @see org.springframework.context.ApplicationListener#onApplicationEvent
|
||||
*/
|
||||
public class GenericApplicationListenerAdapter implements SmartApplicationListener {
|
||||
|
||||
private final ApplicationListener delegate;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new GenericApplicationListener for the given delegate.
|
||||
* @param delegate the delegate listener to be invoked
|
||||
*/
|
||||
public GenericApplicationListenerAdapter(ApplicationListener delegate) {
|
||||
Assert.notNull(delegate, "Delegate listener must not be null");
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onApplicationEvent(ApplicationEvent event) {
|
||||
this.delegate.onApplicationEvent(event);
|
||||
}
|
||||
|
||||
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
|
||||
return getGenericEventType(this.delegate.getClass()).isAssignableFrom(eventType);
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return (this.delegate instanceof Ordered ? ((Ordered) this.delegate).getOrder() : Ordered.LOWEST_PRECEDENCE);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Class<? extends ApplicationEvent> getGenericEventType(Class<? extends ApplicationListener> currentClass) {
|
||||
Class classToIntrospect = currentClass;
|
||||
while (classToIntrospect != null) {
|
||||
Type[] ifcs = classToIntrospect.getGenericInterfaces();
|
||||
for (Type ifc : ifcs) {
|
||||
if (ifc instanceof ParameterizedType) {
|
||||
ParameterizedType paramIfc = (ParameterizedType) ifc;
|
||||
Type rawType = paramIfc.getRawType();
|
||||
if (ApplicationListener.class.equals(rawType)) {
|
||||
Type arg = paramIfc.getActualTypeArguments()[0];
|
||||
if (arg instanceof TypeVariable) {
|
||||
arg = GenericTypeResolver.resolveTypeVariable((TypeVariable) arg, this.delegate.getClass());
|
||||
}
|
||||
if (arg instanceof Class) {
|
||||
return (Class) arg;
|
||||
}
|
||||
}
|
||||
else if (ApplicationListener.class.isAssignableFrom((Class) rawType)) {
|
||||
return getGenericEventType((Class) rawType);
|
||||
}
|
||||
}
|
||||
else if (ApplicationListener.class.isAssignableFrom((Class) ifc)) {
|
||||
return getGenericEventType((Class) ifc);
|
||||
}
|
||||
}
|
||||
classToIntrospect = classToIntrospect.getSuperclass();
|
||||
}
|
||||
return ApplicationEvent.class;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package org.springframework.context.event;
|
||||
|
||||
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;
|
||||
@@ -38,13 +39,26 @@ import org.springframework.core.task.SyncTaskExecutor;
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @see #setTaskExecutor
|
||||
* @see #setConcurrentUpdates
|
||||
*/
|
||||
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
|
||||
|
||||
private Executor taskExecutor = new SyncTaskExecutor();
|
||||
|
||||
|
||||
/**
|
||||
* Create a new SimpleApplicationEventMulticaster.
|
||||
*/
|
||||
public SimpleApplicationEventMulticaster() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new SimpleApplicationEventMulticaster for the given BeanFactory.
|
||||
*/
|
||||
public SimpleApplicationEventMulticaster(BeanFactory beanFactory) {
|
||||
setBeanFactory(beanFactory);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the TaskExecutor to execute application listeners with.
|
||||
* <p>Default is a SyncTaskExecutor, executing the listeners synchronously
|
||||
@@ -71,11 +85,16 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM
|
||||
|
||||
public void multicastEvent(final ApplicationEvent event) {
|
||||
for (final ApplicationListener listener : getApplicationListeners()) {
|
||||
getTaskExecutor().execute(new Runnable() {
|
||||
public void run() {
|
||||
listener.onApplicationEvent(event);
|
||||
}
|
||||
});
|
||||
SmartApplicationListener smartListener = (listener instanceof SmartApplicationListener ?
|
||||
(SmartApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
|
||||
if (smartListener.supportsEventType(event.getClass())) {
|
||||
getTaskExecutor().execute(new Runnable() {
|
||||
@SuppressWarnings("unchecked")
|
||||
public void run() {
|
||||
listener.onApplicationEvent(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Extended variant of the standard {@link ApplicationListener} interface,
|
||||
* exposing further metadata such as the supported event type.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface SmartApplicationListener extends ApplicationListener, Ordered {
|
||||
|
||||
/**
|
||||
* Determine whether this listener actually supports the given event type.
|
||||
*/
|
||||
boolean supportsEventType(Class<? extends ApplicationEvent> eventType);
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
@@ -18,6 +18,7 @@ package org.springframework.context.event;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.context.ApplicationListener} decorator that filters
|
||||
@@ -30,11 +31,11 @@ import org.springframework.context.ApplicationListener;
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0.5
|
||||
*/
|
||||
public class SourceFilteringListener implements ApplicationListener {
|
||||
public class SourceFilteringListener implements SmartApplicationListener {
|
||||
|
||||
private final Object source;
|
||||
|
||||
private ApplicationListener delegate;
|
||||
private SmartApplicationListener delegate;
|
||||
|
||||
|
||||
/**
|
||||
@@ -46,7 +47,8 @@ public class SourceFilteringListener implements ApplicationListener {
|
||||
*/
|
||||
public SourceFilteringListener(Object source, ApplicationListener delegate) {
|
||||
this.source = source;
|
||||
this.delegate = delegate;
|
||||
this.delegate = (delegate instanceof SmartApplicationListener ?
|
||||
(SmartApplicationListener) delegate : new GenericApplicationListenerAdapter(delegate));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,12 +69,22 @@ public class SourceFilteringListener implements ApplicationListener {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
|
||||
return (this.delegate == null || this.delegate.supportsEventType(eventType));
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return (this.delegate != null ? this.delegate.getOrder() : Ordered.LOWEST_PRECEDENCE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Actually process the event, after having filtered according to the
|
||||
* desired event source already.
|
||||
* <p>The default implementation invokes the specified delegate, if any.
|
||||
* @param event the event to process (matching the specified source)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void onApplicationEventInternal(ApplicationEvent event) {
|
||||
if (this.delegate == null) {
|
||||
throw new IllegalStateException(
|
||||
|
||||
@@ -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.
|
||||
@@ -19,7 +19,6 @@ package org.springframework.context.support;
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -657,7 +656,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster();
|
||||
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
|
||||
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
|
||||
@@ -685,13 +684,13 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
protected void registerListeners() {
|
||||
// Register statically specified listeners first.
|
||||
for (ApplicationListener listener : getApplicationListeners()) {
|
||||
addListener(listener);
|
||||
getApplicationEventMulticaster().addApplicationListener(listener);
|
||||
}
|
||||
// Do not initialize FactoryBeans here: We need to leave all regular beans
|
||||
// uninitialized to let post-processors apply to them!
|
||||
Collection<ApplicationListener> lisBeans = getBeansOfType(ApplicationListener.class, true, false).values();
|
||||
for (ApplicationListener lisBean : lisBeans) {
|
||||
addListener(lisBean);
|
||||
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
|
||||
for (final String lisName : listenerBeanNames) {
|
||||
getApplicationEventMulticaster().addApplicationListenerBean(lisName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -924,18 +923,18 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
return getBeanFactory().getBeanNamesForType(type);
|
||||
}
|
||||
|
||||
public String[] getBeanNamesForType(Class type, boolean includePrototypes, boolean allowEagerInit) {
|
||||
return getBeanFactory().getBeanNamesForType(type, includePrototypes, allowEagerInit);
|
||||
public String[] getBeanNamesForType(Class type, boolean includeNonSingletons, boolean allowEagerInit) {
|
||||
return getBeanFactory().getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
|
||||
}
|
||||
|
||||
public <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException {
|
||||
return getBeanFactory().getBeansOfType(type);
|
||||
}
|
||||
|
||||
public <T> Map<String, T> getBeansOfType(Class<T> type, boolean includePrototypes, boolean allowEagerInit)
|
||||
public <T> Map<String, T> getBeansOfType(Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
|
||||
throws BeansException {
|
||||
|
||||
return getBeanFactory().getBeansOfType(type, includePrototypes, allowEagerInit);
|
||||
return getBeanFactory().getBeansOfType(type, includeNonSingletons, allowEagerInit);
|
||||
}
|
||||
|
||||
public Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType)
|
||||
|
||||
Reference in New Issue
Block a user