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 75e5e3d873f..1a86b79e955 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -162,6 +162,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader /** Parent context */ private ApplicationContext parent; + /** Environment used by this context */ + private ConfigurableEnvironment environment; + /** BeanFactoryPostProcessors to apply on refresh */ private final List beanFactoryPostProcessors = new ArrayList(); @@ -194,10 +197,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader private ApplicationEventMulticaster applicationEventMulticaster; /** Statically specified listeners */ - private Set> applicationListeners = new LinkedHashSet>(); - - /** Environment used by this context; initialized by {@link #createEnvironment()} */ - private ConfigurableEnvironment environment; + private final Set> applicationListeners = new LinkedHashSet>(); /** @@ -352,7 +352,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader * @return the internal LifecycleProcessor (never {@code null}) * @throws IllegalStateException if the context has not been initialized yet */ - LifecycleProcessor getLifecycleProcessor() { + LifecycleProcessor getLifecycleProcessor() throws IllegalStateException { if (this.lifecycleProcessor == null) { throw new IllegalStateException("LifecycleProcessor not initialized - " + "call 'refresh' before invoking lifecycle methods via the context: " + this); @@ -504,6 +504,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader */ protected void prepareRefresh() { this.startupDate = System.currentTimeMillis(); + this.closed.set(false); this.active.set(true); if (logger.isInfoEnabled()) { @@ -721,11 +722,12 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader for (ApplicationListener listener : getApplicationListeners()) { getApplicationEventMulticaster().addApplicationListener(listener); } + // Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let post-processors apply to them! String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false); - for (String lisName : listenerBeanNames) { - getApplicationEventMulticaster().addApplicationListenerBean(lisName); + for (String listenerBeanName : listenerBeanNames) { + getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName); } } @@ -801,7 +803,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader this.shutdownHook = new Thread() { @Override public void run() { - doClose(); + synchronized (startupShutdownMonitor) { + doClose(); + } } }; Runtime.getRuntime().addShutdownHook(this.shutdownHook); diff --git a/spring-context/src/test/java/org/springframework/context/support/ClassPathXmlApplicationContextTests.java b/spring-context/src/test/java/org/springframework/context/support/ClassPathXmlApplicationContextTests.java index 4b35c7a6338..717d36ba932 100644 --- a/spring-context/src/test/java/org/springframework/context/support/ClassPathXmlApplicationContextTests.java +++ b/spring-context/src/test/java/org/springframework/context/support/ClassPathXmlApplicationContextTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -49,7 +49,7 @@ import static org.junit.Assert.*; * @author Juergen Hoeller * @author Chris Beams */ -public final class ClassPathXmlApplicationContextTests { +public class ClassPathXmlApplicationContextTests { private static final String PATH = "/org/springframework/context/support/"; private static final String RESOURCE_CONTEXT = PATH + "ClassPathXmlApplicationContextTests-resource.xml"; @@ -82,13 +82,23 @@ public final class ClassPathXmlApplicationContextTests { @Test public void testMultipleConfigLocations() { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext( - new String[] { FQ_CONTEXT_B, FQ_CONTEXT_C, FQ_CONTEXT_A}); + FQ_CONTEXT_B, FQ_CONTEXT_C, FQ_CONTEXT_A); assertTrue(ctx.containsBean("service")); assertTrue(ctx.containsBean("logicOne")); assertTrue(ctx.containsBean("logicTwo")); + + // re-refresh (after construction refresh) Service service = (Service) ctx.getBean("service"); ctx.refresh(); assertTrue(service.isProperlyDestroyed()); + + // regular close call + service = (Service) ctx.getBean("service"); + ctx.close(); + assertTrue(service.isProperlyDestroyed()); + + // re-activating and re-closing the context (SPR-13425) + ctx.refresh(); service = (Service) ctx.getBean("service"); ctx.close(); assertTrue(service.isProperlyDestroyed()); @@ -107,8 +117,7 @@ public final class ClassPathXmlApplicationContextTests { @Test public void testSingleConfigLocationWithClass() { - ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext( - SIMPLE_CONTEXT, getClass()); + ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(SIMPLE_CONTEXT, getClass()); assertTrue(ctx.containsBean("someMessageSource")); ctx.close(); } @@ -116,7 +125,7 @@ public final class ClassPathXmlApplicationContextTests { @Test public void testAliasWithPlaceholder() { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext( - new String[] { FQ_CONTEXT_B, FQ_ALIASED_CONTEXT_C, FQ_CONTEXT_A}); + FQ_CONTEXT_B, FQ_ALIASED_CONTEXT_C, FQ_CONTEXT_A); assertTrue(ctx.containsBean("service")); assertTrue(ctx.containsBean("logicOne")); assertTrue(ctx.containsBean("logicTwo")); @@ -144,16 +153,14 @@ public final class ClassPathXmlApplicationContextTests { private void checkExceptionFromInvalidValueType(Throwable ex) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ex.printStackTrace(new PrintStream(baos)); - String dump = FileCopyUtils.copyToString( - new InputStreamReader(new ByteArrayInputStream(baos.toByteArray()))); + String dump = FileCopyUtils.copyToString(new InputStreamReader(new ByteArrayInputStream(baos.toByteArray()))); assertTrue(dump.contains("someMessageSource")); assertTrue(dump.contains("useCodeAsDefaultMessage")); } @Test public void testContextWithInvalidLazyClass() { - ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext( - INVALID_CLASS_CONTEXT, getClass()); + ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(INVALID_CLASS_CONTEXT, getClass()); assertTrue(ctx.containsBean("someMessageSource")); try { ctx.getBean("someMessageSource"); @@ -167,8 +174,7 @@ public final class ClassPathXmlApplicationContextTests { @Test public void testContextWithClassNameThatContainsPlaceholder() { - ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext( - CLASS_WITH_PLACEHOLDER_CONTEXT, getClass()); + ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(CLASS_WITH_PLACEHOLDER_CONTEXT, getClass()); assertTrue(ctx.containsBean("someMessageSource")); assertTrue(ctx.getBean("someMessageSource") instanceof StaticMessageSource); ctx.close(); @@ -282,7 +288,7 @@ public final class ClassPathXmlApplicationContextTests { @Test public void testAliasThatOverridesEarlierBean() { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext( - new String[] {FQ_SIMPLE_CONTEXT, ALIAS_THAT_OVERRIDES_PARENT_CONTEXT}); + FQ_SIMPLE_CONTEXT, ALIAS_THAT_OVERRIDES_PARENT_CONTEXT); Object myMs = ctx.getBean("myMessageSource"); Object someMs2 = ctx.getBean("someMessageSource"); assertSame(myMs, someMs2);