Description
Sam Brannen opened SPR-12932 and commented
Status Quo
It is currently possible to autowire against an ApplicationContext
that has been closed. However, attempting to do so should result in some sort of exception being thrown. See the example failing tests below.
This issue was brought to our attention when integration tests in the Spring Integration test suite started failing. See INT-3543 for details.
Analysis by Jürgen Höller
AbstractApplicationContext
already invokes assertBeanFactoryActive()
before it delegates to the internal DefaultListableBeanFactory
but of course only for methods on the ApplicationContext
itself.
The underlying BeanFactory
doesn't have the notion of a 'closed' state -- it may just have had its singletons destroyed but could, from its perspective, recreate them at any point.
The semantically cleaner solution is to perform the assertion in the application context's getAutowireCapableBeanFactory()
implementation: AbstractRefreshableApplicationContext
subclasses do throw an IllegalStateException
there after closing, but GenericApplicationContext
subclasses don't. The latter needs to be fixed, and the getAutowireCapableBeanFactory()
Javadoc in the ApplicationContext
interface needs to explicitly state that an IllegalStateException
will be thrown after context closing.
Example Failing Tests
Example1Test
does not fail until the second line of the test2()
method, thus demonstrating that the test instance was autowired from a closed ApplicationContext
.
Example2Test
contains a copy of the actual code from DependencyInjectionTestExecutionListener.injectDependencies()
, demonstrating that autowiring against a closed ApplicationContext
in fact does not throw any kind of exception.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class Example1Test {
@Configuration static class Config {
@Bean String foo() { return "foo"; }
}
@Autowired ConfigurableApplicationContext context;
@Autowired String foo;
@Test
public void test1() {
assertEquals("foo", foo);
assertTrue(context.isActive());
}
@After
public void close() {
context.close();
}
@Test
public void test2() {
assertEquals("foo", foo);
assertTrue(context.isActive());
}
}
public class Example2Test {
@Test(expected = Exception.class)
public void autowiringAgainstClosedContextShouldThrowAnException() {
ConfigurableApplicationContext context = new GenericApplicationContext();
context.refresh();
context.close();
// Simulate DependencyInjectionTestExecutionListener
AutowireCapableBeanFactory beanFactory = context.getAutowireCapableBeanFactory();
beanFactory.autowireBeanProperties(this, AutowireCapableBeanFactory.AUTOWIRE_NO, false);
}
}
Affects: 4.1.2, 4.1.6
Issue Links:
- INT-3543 Fix tests do not close ApplicationContext between test methods
- ApplicationContext fails to load in TestNG test if previous test is annotated with @DirtiesContext [SPR-12918] #17517 ApplicationContext fails to load in TestNG test if previous test is annotated with
@DirtiesContext
- DefaultListableBeanFactory should allow efficient access to current bean names [SPR-12404] #17012 DefaultListableBeanFactory should allow efficient access to current bean names