AbstractApplicationEventMulticaster filters listeners against their type first, avoiding eager retrieval of listener instances for non-matching events
Issue: SPR-11501
This commit is contained in:
parent
fa44224430
commit
cb41f42791
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2014 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.
|
||||||
|
@ -162,12 +162,16 @@ public abstract class AbstractApplicationEventMulticaster implements Application
|
||||||
BeanFactory beanFactory = getBeanFactory();
|
BeanFactory beanFactory = getBeanFactory();
|
||||||
for (String listenerBeanName : listenerBeans) {
|
for (String listenerBeanName : listenerBeans) {
|
||||||
try {
|
try {
|
||||||
ApplicationListener<?> listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
|
Class<?> listenerType = beanFactory.getType(listenerBeanName);
|
||||||
|
if (listenerType == null || supportsEvent(listenerType, event)) {
|
||||||
|
ApplicationListener<?> listener =
|
||||||
|
beanFactory.getBean(listenerBeanName, ApplicationListener.class);
|
||||||
if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
|
if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
|
||||||
retriever.applicationListenerBeans.add(listenerBeanName);
|
retriever.applicationListenerBeans.add(listenerBeanName);
|
||||||
allListeners.add(listener);
|
allListeners.add(listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
catch (NoSuchBeanDefinitionException ex) {
|
catch (NoSuchBeanDefinitionException ex) {
|
||||||
// Singleton listener instance (without backing bean definition) disappeared -
|
// Singleton listener instance (without backing bean definition) disappeared -
|
||||||
// probably in the middle of the destruction phase
|
// probably in the middle of the destruction phase
|
||||||
|
@ -180,6 +184,25 @@ public abstract class AbstractApplicationEventMulticaster implements Application
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter a listener early through checking its generically declared event
|
||||||
|
* type before trying to instantiate it.
|
||||||
|
* <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
|
||||||
|
* {@link #supportsEvent(ApplicationListener, Class, Class)} call afterwards.
|
||||||
|
* @param listenerType the listener's type as determined by the BeanFactory
|
||||||
|
* @param event the event to check
|
||||||
|
* @return whether the given listener should be included in the candidates
|
||||||
|
* for the given event type
|
||||||
|
*/
|
||||||
|
protected boolean supportsEvent(Class<?> listenerType, ApplicationEvent event) {
|
||||||
|
if (SmartApplicationListener.class.isAssignableFrom(listenerType)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Class<?> declaredEventType = GenericApplicationListenerAdapter.resolveDeclaredEventType(listenerType);
|
||||||
|
return (declaredEventType == null || declaredEventType.isInstance(event));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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}
|
||||||
|
@ -189,8 +212,8 @@ public abstract class AbstractApplicationEventMulticaster implements Application
|
||||||
* @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
|
||||||
* @return whether the given listener should be included in the
|
* @return whether the given listener should be included in the candidates
|
||||||
* candidates 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) {
|
Class<? extends ApplicationEvent> eventType, Class<?> sourceType) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2014 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.
|
||||||
|
@ -54,14 +54,14 @@ public class GenericApplicationListenerAdapter implements SmartApplicationListen
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
|
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
|
||||||
Class<?> typeArg = GenericTypeResolver.resolveTypeArgument(this.delegate.getClass(), ApplicationListener.class);
|
Class<?> declaredEventType = resolveDeclaredEventType(this.delegate.getClass());
|
||||||
if (typeArg == null || typeArg.equals(ApplicationEvent.class)) {
|
if (declaredEventType == null || declaredEventType.equals(ApplicationEvent.class)) {
|
||||||
Class<?> targetClass = AopUtils.getTargetClass(this.delegate);
|
Class<?> targetClass = AopUtils.getTargetClass(this.delegate);
|
||||||
if (targetClass != this.delegate.getClass()) {
|
if (targetClass != this.delegate.getClass()) {
|
||||||
typeArg = GenericTypeResolver.resolveTypeArgument(targetClass, ApplicationListener.class);
|
declaredEventType = resolveDeclaredEventType(targetClass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (typeArg == null || typeArg.isAssignableFrom(eventType));
|
return (declaredEventType == null || declaredEventType.isAssignableFrom(eventType));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -74,4 +74,9 @@ 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 Class<?> resolveDeclaredEventType(Class<?> listenerType) {
|
||||||
|
return GenericTypeResolver.resolveTypeArgument(listenerType, ApplicationListener.class);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2014 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.
|
||||||
|
@ -110,14 +110,18 @@ public class ApplicationContextEventTests {
|
||||||
context.registerBeanDefinition("listener1", new RootBeanDefinition(MyOrderedListener1.class));
|
context.registerBeanDefinition("listener1", new RootBeanDefinition(MyOrderedListener1.class));
|
||||||
RootBeanDefinition listener2 = new RootBeanDefinition(MyOrderedListener2.class);
|
RootBeanDefinition listener2 = new RootBeanDefinition(MyOrderedListener2.class);
|
||||||
listener2.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference("listener1"));
|
listener2.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference("listener1"));
|
||||||
|
listener2.setLazyInit(true);
|
||||||
context.registerBeanDefinition("listener2", listener2);
|
context.registerBeanDefinition("listener2", listener2);
|
||||||
context.refresh();
|
context.refresh();
|
||||||
|
assertFalse(context.getDefaultListableBeanFactory().containsSingleton("listener2"));
|
||||||
|
|
||||||
MyOrderedListener1 listener1 = context.getBean("listener1", MyOrderedListener1.class);
|
MyOrderedListener1 listener1 = context.getBean("listener1", MyOrderedListener1.class);
|
||||||
MyEvent event1 = new MyEvent(context);
|
MyOtherEvent event1 = new MyOtherEvent(context);
|
||||||
context.publishEvent(event1);
|
context.publishEvent(event1);
|
||||||
MyOtherEvent event2 = new MyOtherEvent(context);
|
assertFalse(context.getDefaultListableBeanFactory().containsSingleton("listener2"));
|
||||||
|
MyEvent event2 = new MyEvent(context);
|
||||||
context.publishEvent(event2);
|
context.publishEvent(event2);
|
||||||
|
assertTrue(context.getDefaultListableBeanFactory().containsSingleton("listener2"));
|
||||||
MyEvent event3 = new MyEvent(context);
|
MyEvent event3 = new MyEvent(context);
|
||||||
context.publishEvent(event3);
|
context.publishEvent(event3);
|
||||||
MyOtherEvent event4 = new MyOtherEvent(context);
|
MyOtherEvent event4 = new MyOtherEvent(context);
|
||||||
|
|
Loading…
Reference in New Issue