Browse Source

AbstractApplicationEventMulticaster filters listeners against their type first, avoiding eager retrieval of listener instances for non-matching events

Issue: SPR-11501
pull/477/merge
Juergen Hoeller 11 years ago
parent
commit
cb41f42791
  1. 37
      spring-context/src/main/java/org/springframework/context/event/AbstractApplicationEventMulticaster.java
  2. 15
      spring-context/src/main/java/org/springframework/context/event/GenericApplicationListenerAdapter.java
  3. 10
      spring-context/src/test/java/org/springframework/context/event/ApplicationContextEventTests.java

37
spring-context/src/main/java/org/springframework/context/event/AbstractApplicationEventMulticaster.java

@ -1,5 +1,5 @@ @@ -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");
* you may not use this file except in compliance with the License.
@ -162,10 +162,14 @@ public abstract class AbstractApplicationEventMulticaster implements Application @@ -162,10 +162,14 @@ public abstract class AbstractApplicationEventMulticaster implements Application
BeanFactory beanFactory = getBeanFactory();
for (String listenerBeanName : listenerBeans) {
try {
ApplicationListener<?> listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
retriever.applicationListenerBeans.add(listenerBeanName);
allListeners.add(listener);
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)) {
retriever.applicationListenerBeans.add(listenerBeanName);
allListeners.add(listener);
}
}
}
catch (NoSuchBeanDefinitionException ex) {
@ -180,6 +184,25 @@ public abstract class AbstractApplicationEventMulticaster implements Application @@ -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.
* <p>The default implementation detects the {@link SmartApplicationListener}
@ -189,8 +212,8 @@ public abstract class AbstractApplicationEventMulticaster implements Application @@ -189,8 +212,8 @@ public abstract class AbstractApplicationEventMulticaster implements Application
* @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
* @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) {

15
spring-context/src/main/java/org/springframework/context/event/GenericApplicationListenerAdapter.java

@ -1,5 +1,5 @@ @@ -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");
* you may not use this file except in compliance with the License.
@ -54,14 +54,14 @@ public class GenericApplicationListenerAdapter implements SmartApplicationListen @@ -54,14 +54,14 @@ public class GenericApplicationListenerAdapter implements SmartApplicationListen
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
Class<?> typeArg = GenericTypeResolver.resolveTypeArgument(this.delegate.getClass(), ApplicationListener.class);
if (typeArg == null || typeArg.equals(ApplicationEvent.class)) {
Class<?> declaredEventType = resolveDeclaredEventType(this.delegate.getClass());
if (declaredEventType == null || declaredEventType.equals(ApplicationEvent.class)) {
Class<?> targetClass = AopUtils.getTargetClass(this.delegate);
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
@ -74,4 +74,9 @@ public class GenericApplicationListenerAdapter implements SmartApplicationListen @@ -74,4 +74,9 @@ public class GenericApplicationListenerAdapter implements SmartApplicationListen
return (this.delegate instanceof Ordered ? ((Ordered) this.delegate).getOrder() : Ordered.LOWEST_PRECEDENCE);
}
static Class<?> resolveDeclaredEventType(Class<?> listenerType) {
return GenericTypeResolver.resolveTypeArgument(listenerType, ApplicationListener.class);
}
}

10
spring-context/src/test/java/org/springframework/context/event/ApplicationContextEventTests.java

@ -1,5 +1,5 @@ @@ -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");
* you may not use this file except in compliance with the License.
@ -110,14 +110,18 @@ public class ApplicationContextEventTests { @@ -110,14 +110,18 @@ public class ApplicationContextEventTests {
context.registerBeanDefinition("listener1", new RootBeanDefinition(MyOrderedListener1.class));
RootBeanDefinition listener2 = new RootBeanDefinition(MyOrderedListener2.class);
listener2.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference("listener1"));
listener2.setLazyInit(true);
context.registerBeanDefinition("listener2", listener2);
context.refresh();
assertFalse(context.getDefaultListableBeanFactory().containsSingleton("listener2"));
MyOrderedListener1 listener1 = context.getBean("listener1", MyOrderedListener1.class);
MyEvent event1 = new MyEvent(context);
MyOtherEvent event1 = new MyOtherEvent(context);
context.publishEvent(event1);
MyOtherEvent event2 = new MyOtherEvent(context);
assertFalse(context.getDefaultListableBeanFactory().containsSingleton("listener2"));
MyEvent event2 = new MyEvent(context);
context.publishEvent(event2);
assertTrue(context.getDefaultListableBeanFactory().containsSingleton("listener2"));
MyEvent event3 = new MyEvent(context);
context.publishEvent(event3);
MyOtherEvent event4 = new MyOtherEvent(context);

Loading…
Cancel
Save