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
git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@607 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
parent
ed42fb056e
commit
47f3b9881b
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -16,19 +16,16 @@
|
|||
|
||||
package org.springframework.context.event;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.easymock.EasyMock;
|
||||
import static org.easymock.EasyMock.*;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
import org.junit.Assert;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.FatalBeanException;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.BeanCurrentlyInCreationException;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
|
@ -37,6 +34,7 @@ import org.springframework.context.ApplicationListener;
|
|||
import org.springframework.context.BeanThatBroadcasts;
|
||||
import org.springframework.context.BeanThatListens;
|
||||
import org.springframework.context.support.StaticApplicationContext;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
/**
|
||||
* Unit and integration tests for the ApplicationContext event support.
|
||||
|
|
@ -46,70 +44,9 @@ import org.springframework.context.support.StaticApplicationContext;
|
|||
*/
|
||||
public class ApplicationContextEventTests {
|
||||
|
||||
private AbstractApplicationEventMulticaster getMulticaster() {
|
||||
return new AbstractApplicationEventMulticaster() {
|
||||
public void multicastEvent(ApplicationEvent event) {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multicasterNewCollectionClass() {
|
||||
AbstractApplicationEventMulticaster mc = getMulticaster();
|
||||
|
||||
mc.addApplicationListener(new NoOpApplicationListener());
|
||||
|
||||
mc.setCollectionClass(ArrayList.class);
|
||||
|
||||
assertEquals(1, mc.getApplicationListeners().size());
|
||||
assertEquals(ArrayList.class, mc.getApplicationListeners().getClass());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void multicasterInvalidCollectionClass_NotEvenACollectionType() {
|
||||
AbstractApplicationEventMulticaster mc = getMulticaster();
|
||||
mc.setCollectionClass(ApplicationContextEventTests.class);
|
||||
}
|
||||
|
||||
@Test(expected = FatalBeanException.class)
|
||||
public void multicasterInvalidCollectionClass_PassingAnInterfaceNotAConcreteClass() {
|
||||
AbstractApplicationEventMulticaster mc = getMulticaster();
|
||||
mc.setCollectionClass(List.class);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void multicasterNullCollectionClass() {
|
||||
AbstractApplicationEventMulticaster mc = getMulticaster();
|
||||
mc.setCollectionClass(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multicasterRemoveAll() {
|
||||
AbstractApplicationEventMulticaster mc = getMulticaster();
|
||||
mc.addApplicationListener(new NoOpApplicationListener());
|
||||
mc.removeAllListeners();
|
||||
|
||||
assertEquals(0, mc.getApplicationListeners().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multicasterRemoveOne() {
|
||||
AbstractApplicationEventMulticaster mc = getMulticaster();
|
||||
ApplicationListener one = new NoOpApplicationListener();
|
||||
ApplicationListener two = new NoOpApplicationListener();
|
||||
mc.addApplicationListener(one);
|
||||
mc.addApplicationListener(two);
|
||||
|
||||
mc.removeApplicationListener(one);
|
||||
|
||||
assertEquals(1, mc.getApplicationListeners().size());
|
||||
assertTrue("Remaining listener present", mc.getApplicationListeners().contains(two));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleApplicationEventMulticaster() {
|
||||
ApplicationListener listener = EasyMock.createMock(ApplicationListener.class);
|
||||
|
||||
ApplicationEvent evt = new ContextClosedEvent(new StaticApplicationContext());
|
||||
listener.onApplicationEvent(evt);
|
||||
|
||||
|
|
@ -117,14 +54,25 @@ public class ApplicationContextEventTests {
|
|||
smc.addApplicationListener(listener);
|
||||
|
||||
replay(listener);
|
||||
|
||||
smc.multicastEvent(evt);
|
||||
|
||||
verify(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEvenPublicationInterceptor() throws Throwable {
|
||||
public void orderedListeners() {
|
||||
MyOrderedListener1 listener1 = new MyOrderedListener1();
|
||||
MyOrderedListener2 listener2 = new MyOrderedListener2(listener1);
|
||||
|
||||
SimpleApplicationEventMulticaster smc = new SimpleApplicationEventMulticaster();
|
||||
smc.addApplicationListener(listener2);
|
||||
smc.addApplicationListener(listener1);
|
||||
|
||||
smc.multicastEvent(new MyEvent(this));
|
||||
smc.multicastEvent(new MyOtherEvent(this));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEventPublicationInterceptor() throws Throwable {
|
||||
MethodInvocation invocation = EasyMock.createMock(MethodInvocation.class);
|
||||
ApplicationContext ctx = EasyMock.createMock(ApplicationContext.class);
|
||||
|
||||
|
|
@ -134,37 +82,15 @@ public class ApplicationContextEventTests {
|
|||
interceptor.afterPropertiesSet();
|
||||
|
||||
expect(invocation.proceed()).andReturn(new Object());
|
||||
|
||||
expect(invocation.getThis()).andReturn(new Object());
|
||||
|
||||
ctx.publishEvent(isA(MyEvent.class));
|
||||
|
||||
replay(invocation, ctx);
|
||||
|
||||
interceptor.invoke(invocation);
|
||||
|
||||
verify(invocation, ctx);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listenerAndBroadcasterWithUnresolvableCircularReference() {
|
||||
StaticApplicationContext context = new StaticApplicationContext();
|
||||
context.setDisplayName("listener context");
|
||||
context.registerBeanDefinition("broadcaster", new RootBeanDefinition(BeanThatBroadcasts.class));
|
||||
RootBeanDefinition listenerDef = new RootBeanDefinition(BeanThatListens.class);
|
||||
listenerDef.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference("broadcaster"));
|
||||
context.registerBeanDefinition("listener", listenerDef);
|
||||
try {
|
||||
context.refresh();
|
||||
fail("Should have thrown BeanCreationException with nested BeanCurrentlyInCreationException");
|
||||
}
|
||||
catch (BeanCreationException ex) {
|
||||
assertTrue(ex.contains(BeanCurrentlyInCreationException.class));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listenerAndBroadcasterWithResolvableCircularReference() {
|
||||
public void listenerAndBroadcasterWithCircularReference() {
|
||||
StaticApplicationContext context = new StaticApplicationContext();
|
||||
context.registerBeanDefinition("broadcaster", new RootBeanDefinition(BeanThatBroadcasts.class));
|
||||
RootBeanDefinition listenerDef = new RootBeanDefinition(BeanThatListens.class);
|
||||
|
|
@ -177,6 +103,7 @@ public class ApplicationContextEventTests {
|
|||
Assert.assertEquals("The event was not received by the listener", 2, broadcaster.receivedCount);
|
||||
}
|
||||
|
||||
|
||||
public static class MyEvent extends ApplicationEvent {
|
||||
|
||||
public MyEvent(Object source) {
|
||||
|
|
@ -184,11 +111,52 @@ public class ApplicationContextEventTests {
|
|||
}
|
||||
}
|
||||
|
||||
private static final class NoOpApplicationListener implements ApplicationListener {
|
||||
|
||||
public static class MyOtherEvent extends ApplicationEvent {
|
||||
|
||||
public MyOtherEvent(Object source) {
|
||||
super(source);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class MyOrderedListener1 implements ApplicationListener, Ordered {
|
||||
|
||||
public final Set<ApplicationEvent> seenEvents = new HashSet<ApplicationEvent>();
|
||||
|
||||
public void onApplicationEvent(ApplicationEvent event) {
|
||||
this.seenEvents.add(event);
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public interface MyOrderedListenerIfc<E extends ApplicationEvent> extends ApplicationListener<E>, Ordered {
|
||||
}
|
||||
|
||||
|
||||
public static abstract class MyOrderedListenerBase implements MyOrderedListenerIfc<MyEvent> {
|
||||
|
||||
public int getOrder() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class MyOrderedListener2 extends MyOrderedListenerBase {
|
||||
|
||||
private final MyOrderedListener1 otherListener;
|
||||
|
||||
public MyOrderedListener2(MyOrderedListener1 otherListener) {
|
||||
this.otherListener = otherListener;
|
||||
}
|
||||
|
||||
public void onApplicationEvent(MyEvent event) {
|
||||
assertTrue(otherListener.seenEvents.contains(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.
|
||||
|
|
@ -45,7 +45,7 @@ public abstract class GenericCollectionTypeResolver {
|
|||
* @param collectionClass the collection class to introspect
|
||||
* @return the generic type, or <code>null</code> if none
|
||||
*/
|
||||
public static Class getCollectionType(Class collectionClass) {
|
||||
public static Class getCollectionType(Class<? extends Collection> collectionClass) {
|
||||
return extractTypeFromClass(collectionClass, Collection.class, 0);
|
||||
}
|
||||
|
||||
|
|
@ -55,7 +55,7 @@ public abstract class GenericCollectionTypeResolver {
|
|||
* @param mapClass the map class to introspect
|
||||
* @return the generic type, or <code>null</code> if none
|
||||
*/
|
||||
public static Class getMapKeyType(Class mapClass) {
|
||||
public static Class getMapKeyType(Class<? extends Map> mapClass) {
|
||||
return extractTypeFromClass(mapClass, Map.class, 0);
|
||||
}
|
||||
|
||||
|
|
@ -65,7 +65,7 @@ public abstract class GenericCollectionTypeResolver {
|
|||
* @param mapClass the map class to introspect
|
||||
* @return the generic type, or <code>null</code> if none
|
||||
*/
|
||||
public static Class getMapValueType(Class mapClass) {
|
||||
public static Class getMapValueType(Class<? extends Map> mapClass) {
|
||||
return extractTypeFromClass(mapClass, Map.class, 1);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -99,6 +99,18 @@ public abstract class GenericTypeResolver {
|
|||
return (rawType instanceof Class ? (Class) rawType : method.getReturnType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the given type variable against the given class.
|
||||
* @param tv the type variable to resolve
|
||||
* @param clazz the class that defines the type variable
|
||||
* somewhere in its inheritance hierarchy
|
||||
* @return the resolved type that the variable can get replaced with,
|
||||
* or <code>null</code> if none found
|
||||
*/
|
||||
public static Type resolveTypeVariable(TypeVariable tv, Class clazz) {
|
||||
return getTypeVariableMap(clazz).get(tv);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolve the specified generic type against the given TypeVariable map.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -51,18 +51,13 @@ public interface Ordered {
|
|||
/**
|
||||
* Return the order value of this object, with a
|
||||
* higher value meaning greater in terms of sorting.
|
||||
* <p>Normally starting with 0 or 1, with {@link #LOWEST_PRECEDENCE}
|
||||
* indicating greatest. Same order values will result in arbitrary
|
||||
* positions for the affected objects.
|
||||
* <p>Higher value can be interpreted as lower priority,
|
||||
* consequently the first object has highest priority
|
||||
* <p>Normally starting with 0, with <code>Integer.MAX_VALUE</code>
|
||||
* indicating the greatest value. Same order values will result
|
||||
* in arbitrary positions for the affected objects.
|
||||
* <p>Higher values can be interpreted as lower priority. As a
|
||||
* consequence, the object with the lowest value has highest priority
|
||||
* (somewhat analogous to Servlet "load-on-startup" values).
|
||||
* <p>Note that order values below 0 are reserved for framework
|
||||
* purposes. Application-specified values should always be 0 or
|
||||
* greater, with only framework components (internal or third-party)
|
||||
* supposed to use lower values.
|
||||
* @return the order value
|
||||
* @see #LOWEST_PRECEDENCE
|
||||
*/
|
||||
int getOrder();
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -19,6 +19,7 @@ package org.springframework.core;
|
|||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
|
@ -101,12 +102,22 @@ public class GenericCollectionTypeResolverTests extends AbstractGenericsTests {
|
|||
GenericCollectionTypeResolver.getCollectionReturnType(getter));
|
||||
}
|
||||
|
||||
|
||||
private abstract class CustomMap <T> extends AbstractMap<String, Integer> {
|
||||
public void testClassResolution() {
|
||||
assertEquals(String.class, GenericCollectionTypeResolver.getCollectionType(CustomSet.class));
|
||||
assertEquals(String.class, GenericCollectionTypeResolver.getMapKeyType(CustomMap.class));
|
||||
assertEquals(Integer.class, GenericCollectionTypeResolver.getMapValueType(CustomMap.class));
|
||||
}
|
||||
|
||||
|
||||
private abstract class OtherCustomMap <T> implements Map<String, Integer> {
|
||||
private abstract class CustomSet<T> extends AbstractSet<String> {
|
||||
}
|
||||
|
||||
|
||||
private abstract class CustomMap<T> extends AbstractMap<String, Integer> {
|
||||
}
|
||||
|
||||
|
||||
private abstract class OtherCustomMap<T> implements Map<String, Integer> {
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ import org.springframework.beans.BeanUtils;
|
|||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextException;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
|
|
@ -102,7 +101,8 @@ import org.springframework.web.portlet.context.XmlPortletApplicationContext;
|
|||
* @see #setContextConfigLocation
|
||||
* @see #setNamespace
|
||||
*/
|
||||
public abstract class FrameworkPortlet extends GenericPortletBean implements ApplicationListener {
|
||||
public abstract class FrameworkPortlet extends GenericPortletBean
|
||||
implements ApplicationListener<ContextRefreshedEvent> {
|
||||
|
||||
/**
|
||||
* Default context class for FrameworkPortlet.
|
||||
|
|
@ -417,11 +417,9 @@ public abstract class FrameworkPortlet extends GenericPortletBean implements App
|
|||
* triggering a refresh of this servlet's context-dependent state.
|
||||
* @param event the incoming ApplicationContext event
|
||||
*/
|
||||
public void onApplicationEvent(ApplicationEvent event) {
|
||||
if (event instanceof ContextRefreshedEvent) {
|
||||
this.refreshEventReceived = true;
|
||||
onRefresh(((ContextRefreshedEvent) event).getApplicationContext());
|
||||
}
|
||||
public void onApplicationEvent(ContextRefreshedEvent event) {
|
||||
this.refreshEventReceived = true;
|
||||
onRefresh(event.getApplicationContext());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ import org.springframework.beans.BeanUtils;
|
|||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextException;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
|
|
@ -94,7 +93,8 @@ import org.springframework.web.util.WebUtils;
|
|||
* @see #setContextConfigLocation
|
||||
* @see #setNamespace
|
||||
*/
|
||||
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationListener {
|
||||
public abstract class FrameworkServlet extends HttpServletBean
|
||||
implements ApplicationListener<ContextRefreshedEvent> {
|
||||
|
||||
/**
|
||||
* Suffix for WebApplicationContext namespaces. If a servlet of this class is
|
||||
|
|
@ -493,11 +493,9 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic
|
|||
* triggering a refresh of this servlet's context-dependent state.
|
||||
* @param event the incoming ApplicationContext event
|
||||
*/
|
||||
public void onApplicationEvent(ApplicationEvent event) {
|
||||
if (event instanceof ContextRefreshedEvent) {
|
||||
this.refreshEventReceived = true;
|
||||
onRefresh(((ContextRefreshedEvent) event).getApplicationContext());
|
||||
}
|
||||
public void onApplicationEvent(ContextRefreshedEvent event) {
|
||||
this.refreshEventReceived = true;
|
||||
onRefresh(event.getApplicationContext());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in New Issue