diff --git a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java index 4387432d9a..666dd00e81 100644 --- a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java @@ -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); diff --git a/spring-context/src/main/java/org/springframework/context/support/PostProcessorRegistrationDelegate.java b/spring-context/src/main/java/org/springframework/context/support/PostProcessorRegistrationDelegate.java index f0b4a4b5a9..b2071def0c 100644 --- a/spring-context/src/main/java/org/springframework/context/support/PostProcessorRegistrationDelegate.java +++ b/spring-context/src/main/java/org/springframework/context/support/PostProcessorRegistrationDelegate.java @@ -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 singletonNames = new ConcurrentHashMap(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); + } + } } } diff --git a/spring-context/src/test/java/org/springframework/context/event/ApplicationContextEventTests.java b/spring-context/src/test/java/org/springframework/context/event/ApplicationContextEventTests.java index 24c1a4da95..2bf1053227 100644 --- a/spring-context/src/test/java/org/springframework/context/event/ApplicationContextEventTests.java +++ b/spring-context/src/test/java/org/springframework/context/event/ApplicationContextEventTests.java @@ -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(); }