AbstractApplicationContext's ApplicationListenerDetector removes listeners from ApplicationEventMulticaster on individual destruction

Issue: SPR-7856
This commit is contained in:
Juergen Hoeller
2013-10-30 12:30:13 +01:00
parent eddb125836
commit 0fe49629c0
3 changed files with 52 additions and 7 deletions

View File

@@ -341,7 +341,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
* @return the internal ApplicationEventMulticaster (never {@code null})
* @throws IllegalStateException if the context has not been initialized yet
*/
private ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
if (this.applicationEventMulticaster == null) {
throw new IllegalStateException("ApplicationEventMulticaster not initialized - " +
"call 'refresh' before multicasting events via the context: " + this);
@@ -354,7 +354,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
* @return the internal LifecycleProcessor (never {@code null})
* @throws IllegalStateException if the context has not been initialized yet
*/
private LifecycleProcessor getLifecycleProcessor() {
LifecycleProcessor getLifecycleProcessor() {
if (this.lifecycleProcessor == null) {
throw new IllegalStateException("LifecycleProcessor not initialized - " +
"call 'refresh' before invoking lifecycle methods via the context: " + this);

View File

@@ -31,12 +31,12 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
@@ -177,7 +177,7 @@ class PostProcessorRegistrationDelegate {
}
public static void registerBeanPostProcessors(
ConfigurableListableBeanFactory beanFactory, ConfigurableApplicationContext applicationContext) {
ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
@@ -319,15 +319,15 @@ class PostProcessorRegistrationDelegate {
* BeanPostProcessor that detects beans which implement the ApplicationListener interface.
* This catches beans that can't reliably be detected by getBeanNamesForType.
*/
private static class ApplicationListenerDetector implements MergedBeanDefinitionPostProcessor {
private static class ApplicationListenerDetector implements MergedBeanDefinitionPostProcessor, DestructionAwareBeanPostProcessor {
private static final Log logger = LogFactory.getLog(ApplicationListenerDetector.class);
private final ConfigurableApplicationContext applicationContext;
private final AbstractApplicationContext applicationContext;
private final Map<String, Boolean> singletonNames = new ConcurrentHashMap<String, Boolean>(64);
public ApplicationListenerDetector(ConfigurableApplicationContext applicationContext) {
public ApplicationListenerDetector(AbstractApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@@ -365,6 +365,14 @@ class PostProcessorRegistrationDelegate {
}
return bean;
}
@Override
public void postProcessBeforeDestruction(Object bean, String beanName) {
if (bean instanceof ApplicationListener) {
this.applicationContext.getApplicationEventMulticaster().removeApplicationListener((ApplicationListener) bean);
this.applicationContext.getApplicationEventMulticaster().removeApplicationListenerBean(beanName);
}
}
}
}

View File

@@ -21,6 +21,7 @@ import java.util.Set;
import org.aopalliance.intercept.MethodInvocation;
import org.junit.Test;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.RootBeanDefinition;
@@ -29,6 +30,7 @@ import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.BeanThatBroadcasts;
import org.springframework.context.BeanThatListens;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.tests.sample.beans.TestBean;
@@ -134,6 +136,34 @@ public class ApplicationContextEventTests {
assertTrue(listener1.seenEvents.contains(event2));
assertTrue(listener1.seenEvents.contains(event3));
assertTrue(listener1.seenEvents.contains(event4));
context.close();
}
@Test
public void listenersInApplicationContextWithNestedChild() {
StaticApplicationContext context = new StaticApplicationContext();
RootBeanDefinition nestedChild = new RootBeanDefinition(StaticApplicationContext.class);
nestedChild.getPropertyValues().add("parent", context);
nestedChild.setInitMethodName("refresh");
context.registerBeanDefinition("nestedChild", nestedChild);
RootBeanDefinition listener1Def = new RootBeanDefinition(MyOrderedListener1.class);
listener1Def.setDependsOn(new String[] {"nestedChild"});
context.registerBeanDefinition("listener1", listener1Def);
context.refresh();
MyOrderedListener1 listener1 = context.getBean("listener1", MyOrderedListener1.class);
MyEvent event1 = new MyEvent(context);
context.publishEvent(event1);
assertTrue(listener1.seenEvents.contains(event1));
SimpleApplicationEventMulticaster multicaster = context.getBean(
AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME,
SimpleApplicationEventMulticaster.class);
assertFalse(multicaster.getApplicationListeners().isEmpty());
context.close();
assertTrue(multicaster.getApplicationListeners().isEmpty());
}
@Test
@@ -157,6 +187,8 @@ public class ApplicationContextEventTests {
assertTrue(MyNonSingletonListener.seenEvents.contains(event3));
assertTrue(MyNonSingletonListener.seenEvents.contains(event4));
MyNonSingletonListener.seenEvents.clear();
context.close();
}
@Test
@@ -171,6 +203,8 @@ public class ApplicationContextEventTests {
BeanThatBroadcasts broadcaster = context.getBean("broadcaster", BeanThatBroadcasts.class);
context.publishEvent(new MyEvent(context));
assertEquals("The event was not received by the listener", 2, broadcaster.receivedCount);
context.close();
}
@Test
@@ -180,10 +214,13 @@ public class ApplicationContextEventTests {
listenerDef.getPropertyValues().add("friends", new RootBeanDefinition(BeanThatListens.class));
context.registerBeanDefinition("listener", listenerDef);
context.refresh();
context.publishEvent(new MyEvent(this));
context.publishEvent(new MyEvent(this));
TestBean listener = context.getBean(TestBean.class);
assertEquals(3, ((BeanThatListens) listener.getFriends().iterator().next()).getEventCount());
context.close();
}