From 9d2983e9945a14f79582dc53c03a24f6e9bf927a Mon Sep 17 00:00:00 2001 From: Eric Bottard Date: Mon, 24 Mar 2014 13:56:48 +0100 Subject: [PATCH] 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 --- ...arentContextCloserApplicationListener.java | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/spring-boot/src/main/java/org/springframework/boot/builder/ParentContextCloserApplicationListener.java b/spring-boot/src/main/java/org/springframework/boot/builder/ParentContextCloserApplicationListener.java index 0bede22ad9..dce32b404d 100644 --- a/spring-boot/src/main/java/org/springframework/boot/builder/ParentContextCloserApplicationListener.java +++ b/spring-boot/src/main/java/org/springframework/boot/builder/ParentContextCloserApplicationListener.java @@ -16,6 +16,8 @@ package org.springframework.boot.builder; +import java.lang.ref.WeakReference; + import org.springframework.boot.builder.ParentContextApplicationContextInitializer.ParentContextAvailableEvent; import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; @@ -23,14 +25,13 @@ import org.springframework.context.event.ContextClosedEvent; import org.springframework.core.Ordered; /** - * Listener that closes the application context if its parent is closed. It listens for - * refresh events and grabs the current context from there, and then listens for closed - * events and propagates it down the hierarchy. + * Listener that closes the application context if its parent is closed. It listens for refresh events and grabs the + * current context from there, and then listens for closed events and propagates it down the hierarchy. * * @author Dave Syer */ -public class ParentContextCloserApplicationListener implements - ApplicationListener, Ordered { +public class ParentContextCloserApplicationListener implements ApplicationListener, + Ordered { @Override public int getOrder() { @@ -38,34 +39,39 @@ public class ParentContextCloserApplicationListener implements } @Override - public void onApplicationEvent(ParentContextAvailableEvent event) { + public final void onApplicationEvent(ParentContextAvailableEvent event) { maybeInstallListenerInParent(event.getApplicationContext()); } private void maybeInstallListenerInParent(ConfigurableApplicationContext child) { if (child.getParent() instanceof ConfigurableApplicationContext) { - ConfigurableApplicationContext parent = (ConfigurableApplicationContext) child - .getParent(); - parent.addApplicationListener(new ContextCloserListener(child)); + ConfigurableApplicationContext parent = (ConfigurableApplicationContext) child.getParent(); + parent.addApplicationListener(createContextCloserListener(child)); } } - protected static class ContextCloserListener implements - ApplicationListener { + /** + * 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 { + + private WeakReference contextRef; public ContextCloserListener(ConfigurableApplicationContext context) { - this.context = context; + this.contextRef = new WeakReference(context); } @Override public void onApplicationEvent(ContextClosedEvent event) { - if (this.context != null - && event.getApplicationContext() == this.context.getParent() - && this.context.isActive()) { - this.context.close(); + ConfigurableApplicationContext context = contextRef.get(); + if (context != null && event.getApplicationContext() == context.getParent() && context.isActive()) { + context.close(); } }