hea*_*tar 7 java spring spring-test hierarchical context-configuration
当 Spring 上下文层次结构关闭时,没有保证 bean 被销毁的顺序,这样说是否正确?例如,子上下文中的 bean 将在父上下文之前被销毁。从一个最小的例子来看,上下文的破坏似乎在上下文之间完全不协调(奇怪的是)。两个上下文都注册了一个关闭钩子,该钩子稍后将在不同的线程中执行。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextHierarchy({
@ContextConfiguration(classes = {ATest.Root.class}),
@ContextConfiguration(classes = {ATest.Child.class})
})
public class ATest {
@Test
public void contextTest() {
}
public static class Root {
@Bean
Foo foo() {
return new Foo();
}
}
public static class Child {
@Bean
Bar bar() {
return new Bar();
}
}
static class Foo {
Logger logger = LoggerFactory.getLogger(Foo.class);
volatile boolean destroyed;
@PostConstruct
void setup() {
logger.info("foo setup");
}
@PreDestroy
void destroy() {
destroyed = true;
logger.info("foo destroy");
}
}
static class Bar {
@Autowired
Foo foo;
Logger logger = LoggerFactory.getLogger(Bar.class);
@PostConstruct
void setup() {
logger.info("bar setup with foo = {}", foo);
}
@PreDestroy
void destroy() {
logger.info("bar destroy, foo is destroyed={}", foo.destroyed);
}
}
}
Run Code Online (Sandbox Code Playgroud)
给出输出:
21:38:53.287 [Test worker] INFO ATest$Foo - foo setup
21:38:53.327 [Test worker] INFO ATest$Bar - bar setup with foo = com.tango.citrine.spring.ATest$Foo@2458117b
21:38:53.363 [Thread-4] INFO ATest$Foo - foo destroy
21:38:53.364 [Thread-5] INFO ATest$Bar - bar destroy, foo is destroyed=true
Run Code Online (Sandbox Code Playgroud)
有没有办法强制以“正确”的顺序关闭上下文?
我自己也研究过同样的问题,一切看起来都不再奇怪了。尽管我仍然希望这种行为有所不同。
当您有父级和子级 Spring 上下文时,父级对子级一无所知。这就是 Spring 的设计方式,并且对于所有设置都是如此。
现在可能有一些区别
这种情况最常见的设置(不包括单上下文设置)是使用 声明根上下文并ContextLoaderListener通过 声明子上下文DispatcherServlet。
当 webapp(或容器)关闭时, 和会ContextLoaderListener相应地DispatcherServlet收到通知。ServletContextListener.contextDestroyed(...)Servlet.destroy()
根据javadoc,首先 servlet 和过滤器被销毁,并且只有在它们完成后才被销毁ServletContextListener。
因此,在 Servlet 容器中运行的 Web 应用程序中,DispatcherServlet首先销毁上下文(子上下文),然后才销毁根上下文。
以下内容不仅适用于独立 weapp,也适用于任何使用分层上下文的独立 Spring 应用。
由于没有容器,因此独立应用程序需要与 JVM 本身进行通信,以便接收关闭信号并处理它。这是使用关闭挂钩机制完成的。
除了 JVM 功能\版本之外,Spring 不会尝试推断其运行的环境(但 Spring Boot 在自动推断 env 方面可以做得很好)。
因此,要让 Spring 注册一个关闭钩子,您需要在创建上下文 ( javadoc )时指定它这样做。如果您不这样做,您将根本不会调用@PreDestroy/回调。DisposableBean
一旦您向 JVM 注册了上下文的关闭钩子,它将收到通知并正确处理该上下文的关闭。
如果您有亲子关系,您可能需要.registerShutdownHook()为每个人提供。这对于某些情况有效。但 JVM 以不确定的顺序调用关闭挂钩,因此不幸的是,这并不能真正解决主题问题。
现在我们能做什么呢
可能最简单(尽管不是最优雅)的解决方案是 在父上下文中使用ApplicationListener<ContextClosedEvent>or ,并且DisposableBean
@Autowire或@DependsOn或 xml 对应项来完成)最初的问题提出了 JUnit 测试。我并没有真正挖掘那么深,但我怀疑事情又与这个不同了。因为它SpringJUnit4ClassRunner首先统治着它们和上下文层次结构。另一方面,JUnit 测试具有良好定义和管理的生命周期(几乎是列表 servlet 容器)。
不管怎样,在理解了内部工作原理后,我相信你应该很容易解决这个问题:)
| 归档时间: |
|
| 查看次数: |
7473 次 |
| 最近记录: |