Skip to content

Commit 0fe4962

Browse files
committed
AbstractApplicationContext's ApplicationListenerDetector removes listeners from ApplicationEventMulticaster on individual destruction
Issue: SPR-7856
1 parent eddb125 commit 0fe4962

File tree

3 files changed

+52
-7
lines changed

3 files changed

+52
-7
lines changed

spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ public void publishEvent(ApplicationEvent event) {
341341
* @return the internal ApplicationEventMulticaster (never {@code null})
342342
* @throws IllegalStateException if the context has not been initialized yet
343343
*/
344-
private ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
344+
ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
345345
if (this.applicationEventMulticaster == null) {
346346
throw new IllegalStateException("ApplicationEventMulticaster not initialized - " +
347347
"call 'refresh' before multicasting events via the context: " + this);
@@ -354,7 +354,7 @@ private ApplicationEventMulticaster getApplicationEventMulticaster() throws Ille
354354
* @return the internal LifecycleProcessor (never {@code null})
355355
* @throws IllegalStateException if the context has not been initialized yet
356356
*/
357-
private LifecycleProcessor getLifecycleProcessor() {
357+
LifecycleProcessor getLifecycleProcessor() {
358358
if (this.lifecycleProcessor == null) {
359359
throw new IllegalStateException("LifecycleProcessor not initialized - " +
360360
"call 'refresh' before invoking lifecycle methods via the context: " + this);

spring-context/src/main/java/org/springframework/context/support/PostProcessorRegistrationDelegate.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@
3131
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
3232
import org.springframework.beans.factory.config.BeanPostProcessor;
3333
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
34+
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
3435
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
3536
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
3637
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
3738
import org.springframework.beans.factory.support.RootBeanDefinition;
3839
import org.springframework.context.ApplicationListener;
39-
import org.springframework.context.ConfigurableApplicationContext;
4040
import org.springframework.core.OrderComparator;
4141
import org.springframework.core.Ordered;
4242
import org.springframework.core.PriorityOrdered;
@@ -177,7 +177,7 @@ else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
177177
}
178178

179179
public static void registerBeanPostProcessors(
180-
ConfigurableListableBeanFactory beanFactory, ConfigurableApplicationContext applicationContext) {
180+
ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
181181

182182
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
183183

@@ -319,15 +319,15 @@ public Object postProcessAfterInitialization(Object bean, String beanName) {
319319
* BeanPostProcessor that detects beans which implement the ApplicationListener interface.
320320
* This catches beans that can't reliably be detected by getBeanNamesForType.
321321
*/
322-
private static class ApplicationListenerDetector implements MergedBeanDefinitionPostProcessor {
322+
private static class ApplicationListenerDetector implements MergedBeanDefinitionPostProcessor, DestructionAwareBeanPostProcessor {
323323

324324
private static final Log logger = LogFactory.getLog(ApplicationListenerDetector.class);
325325

326-
private final ConfigurableApplicationContext applicationContext;
326+
private final AbstractApplicationContext applicationContext;
327327

328328
private final Map<String, Boolean> singletonNames = new ConcurrentHashMap<String, Boolean>(64);
329329

330-
public ApplicationListenerDetector(ConfigurableApplicationContext applicationContext) {
330+
public ApplicationListenerDetector(AbstractApplicationContext applicationContext) {
331331
this.applicationContext = applicationContext;
332332
}
333333

@@ -365,6 +365,14 @@ else if (flag == null) {
365365
}
366366
return bean;
367367
}
368+
369+
@Override
370+
public void postProcessBeforeDestruction(Object bean, String beanName) {
371+
if (bean instanceof ApplicationListener) {
372+
this.applicationContext.getApplicationEventMulticaster().removeApplicationListener((ApplicationListener) bean);
373+
this.applicationContext.getApplicationEventMulticaster().removeApplicationListenerBean(beanName);
374+
}
375+
}
368376
}
369377

370378
}

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import org.aopalliance.intercept.MethodInvocation;
2323
import org.junit.Test;
24+
2425
import org.springframework.aop.framework.ProxyFactory;
2526
import org.springframework.beans.factory.config.RuntimeBeanReference;
2627
import org.springframework.beans.factory.support.RootBeanDefinition;
@@ -29,6 +30,7 @@
2930
import org.springframework.context.ApplicationListener;
3031
import org.springframework.context.BeanThatBroadcasts;
3132
import org.springframework.context.BeanThatListens;
33+
import org.springframework.context.support.AbstractApplicationContext;
3234
import org.springframework.context.support.StaticApplicationContext;
3335
import org.springframework.core.Ordered;
3436
import org.springframework.tests.sample.beans.TestBean;
@@ -134,6 +136,34 @@ public void listenersInApplicationContext() {
134136
assertTrue(listener1.seenEvents.contains(event2));
135137
assertTrue(listener1.seenEvents.contains(event3));
136138
assertTrue(listener1.seenEvents.contains(event4));
139+
140+
context.close();
141+
}
142+
143+
@Test
144+
public void listenersInApplicationContextWithNestedChild() {
145+
StaticApplicationContext context = new StaticApplicationContext();
146+
RootBeanDefinition nestedChild = new RootBeanDefinition(StaticApplicationContext.class);
147+
nestedChild.getPropertyValues().add("parent", context);
148+
nestedChild.setInitMethodName("refresh");
149+
context.registerBeanDefinition("nestedChild", nestedChild);
150+
RootBeanDefinition listener1Def = new RootBeanDefinition(MyOrderedListener1.class);
151+
listener1Def.setDependsOn(new String[] {"nestedChild"});
152+
context.registerBeanDefinition("listener1", listener1Def);
153+
context.refresh();
154+
155+
MyOrderedListener1 listener1 = context.getBean("listener1", MyOrderedListener1.class);
156+
MyEvent event1 = new MyEvent(context);
157+
context.publishEvent(event1);
158+
assertTrue(listener1.seenEvents.contains(event1));
159+
160+
SimpleApplicationEventMulticaster multicaster = context.getBean(
161+
AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME,
162+
SimpleApplicationEventMulticaster.class);
163+
assertFalse(multicaster.getApplicationListeners().isEmpty());
164+
165+
context.close();
166+
assertTrue(multicaster.getApplicationListeners().isEmpty());
137167
}
138168

139169
@Test
@@ -157,6 +187,8 @@ public void nonSingletonListenerInApplicationContext() {
157187
assertTrue(MyNonSingletonListener.seenEvents.contains(event3));
158188
assertTrue(MyNonSingletonListener.seenEvents.contains(event4));
159189
MyNonSingletonListener.seenEvents.clear();
190+
191+
context.close();
160192
}
161193

162194
@Test
@@ -171,6 +203,8 @@ public void listenerAndBroadcasterWithCircularReference() {
171203
BeanThatBroadcasts broadcaster = context.getBean("broadcaster", BeanThatBroadcasts.class);
172204
context.publishEvent(new MyEvent(context));
173205
assertEquals("The event was not received by the listener", 2, broadcaster.receivedCount);
206+
207+
context.close();
174208
}
175209

176210
@Test
@@ -180,10 +214,13 @@ public void innerBeanAsListener() {
180214
listenerDef.getPropertyValues().add("friends", new RootBeanDefinition(BeanThatListens.class));
181215
context.registerBeanDefinition("listener", listenerDef);
182216
context.refresh();
217+
183218
context.publishEvent(new MyEvent(this));
184219
context.publishEvent(new MyEvent(this));
185220
TestBean listener = context.getBean(TestBean.class);
186221
assertEquals(3, ((BeanThatListens) listener.getFriends().iterator().next()).getEventCount());
222+
223+
context.close();
187224
}
188225

189226

0 commit comments

Comments
 (0)