Prevent retention of child from parent

When the child is listening for parent closed events
the parent has a reference to the listener which contains the chils.
Thust there is a cycle of references and a GC probalem waiting to
happen.

This change breaks the cycle by making the reference to the
child a WeakReference.

Fixes gh-559
This commit is contained in:
Eric Bottard 2014-03-24 13:56:48 +01:00 committed by Dave Syer
parent 770c115d88
commit 9d2983e994
1 changed files with 23 additions and 17 deletions

View File

@ -16,6 +16,8 @@
package org.springframework.boot.builder; package org.springframework.boot.builder;
import java.lang.ref.WeakReference;
import org.springframework.boot.builder.ParentContextApplicationContextInitializer.ParentContextAvailableEvent; import org.springframework.boot.builder.ParentContextApplicationContextInitializer.ParentContextAvailableEvent;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
@ -23,14 +25,13 @@ import org.springframework.context.event.ContextClosedEvent;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
/** /**
* Listener that closes the application context if its parent is closed. It listens for * Listener that closes the application context if its parent is closed. It listens for refresh events and grabs the
* refresh events and grabs the current context from there, and then listens for closed * current context from there, and then listens for closed events and propagates it down the hierarchy.
* events and propagates it down the hierarchy.
* *
* @author Dave Syer * @author Dave Syer
*/ */
public class ParentContextCloserApplicationListener implements public class ParentContextCloserApplicationListener implements ApplicationListener<ParentContextAvailableEvent>,
ApplicationListener<ParentContextAvailableEvent>, Ordered { Ordered {
@Override @Override
public int getOrder() { public int getOrder() {
@ -38,34 +39,39 @@ public class ParentContextCloserApplicationListener implements
} }
@Override @Override
public void onApplicationEvent(ParentContextAvailableEvent event) { public final void onApplicationEvent(ParentContextAvailableEvent event) {
maybeInstallListenerInParent(event.getApplicationContext()); maybeInstallListenerInParent(event.getApplicationContext());
} }
private void maybeInstallListenerInParent(ConfigurableApplicationContext child) { private void maybeInstallListenerInParent(ConfigurableApplicationContext child) {
if (child.getParent() instanceof ConfigurableApplicationContext) { if (child.getParent() instanceof ConfigurableApplicationContext) {
ConfigurableApplicationContext parent = (ConfigurableApplicationContext) child ConfigurableApplicationContext parent = (ConfigurableApplicationContext) child.getParent();
.getParent(); parent.addApplicationListener(createContextCloserListener(child));
parent.addApplicationListener(new ContextCloserListener(child));
} }
} }
protected static class ContextCloserListener implements /**
ApplicationListener<ContextClosedEvent> { * Subclasses may override to create their own subclass of ContextCloserListener. This still enforces the use of a
* weak reference.
*/
protected ContextCloserListener createContextCloserListener(ConfigurableApplicationContext child) {
return new ContextCloserListener(child);
}
private ConfigurableApplicationContext context; protected static class ContextCloserListener implements ApplicationListener<ContextClosedEvent> {
private WeakReference<ConfigurableApplicationContext> contextRef;
public ContextCloserListener(ConfigurableApplicationContext context) { public ContextCloserListener(ConfigurableApplicationContext context) {
this.context = context; this.contextRef = new WeakReference<ConfigurableApplicationContext>(context);
} }
@Override @Override
public void onApplicationEvent(ContextClosedEvent event) { public void onApplicationEvent(ContextClosedEvent event) {
if (this.context != null ConfigurableApplicationContext context = contextRef.get();
&& event.getApplicationContext() == this.context.getParent() if (context != null && event.getApplicationContext() == context.getParent() && context.isActive()) {
&& this.context.isActive()) { context.close();
this.context.close();
} }
} }