[英]Order of bean destruction in Spring context hierarchy
說當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);
}
}
}
給出輸出:
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
有沒有辦法強制以“正確”的順序關閉上下文?
我自己也在同一個問題上挖過,所有這些看起來都不再怪異。 雖然我仍然希望這種行為有所不同。
當你有父母和孩子的春天上下文時,父母對孩子一無所知。 這就是Spring的設計方式,這適用於所有設置。
現在可能會有一些區別
這種情況下最常見的設置(不計算單上下文設置)是通過DispatcherServlet
使用ContextLoaderListener
和子上下文聲明根上下文。
當關閉webapp(或容器)時, ContextLoaderListener
和DispatcherServlet
都會相應地通過ServletContextListener.contextDestroyed(...)
和Servlet.destroy()
接收通知。
根據javadoc,首先servlet和過濾器被破壞,並且只有在它們完成ServletContextListener
之后才會被破壞。
因此,在servlet容器中運行的webapps中,首先銷毀DispatcherServlet
上下文(它是一個子上下文),然后銷毀根上下文。
以下內容不僅適用於獨立的weapps,也適用於任何使用分層上下文的獨立Spring應用程序。
由於沒有容器,因此stanadlone應用程序需要與JVM本身通信才能接收關閉信號並處理它。 這是使用shutdown hooks機制完成的。
除了JVM功能\\版本之外,Spring並沒有嘗試推斷它所運行的環境(但是Spring Boot可以在自動推導env方面做得很好)。
因此,要使Spring注冊一個關閉鈎子,您需要在創建上下文( javadoc )時這樣做。 如果不這樣做,則根本不會調用@PreDestroy
/ DisposableBean
回調。
一旦您向JVM注冊了上下文的關閉掛鈎,它將被通知並將正確處理該上下文的關閉。
如果您有父子環境,則可能需要為每個子環境設置.registerShutdownHook()
。 這適用於某些情況。 但是JVM以非確定性順序調用了關閉鈎子,所以不幸的是,這並沒有真正解決主題問題。
那我們能做些什么呢?
可能最簡單(盡管不是最優雅)的解決方案是將ApplicationListener<ContextClosedEvent>
或DisposableBean
放在父上下文中,
@Autowire
或@DependsOn
或xml對應的方式完成) 最初的問題提出了一個JUnit測試。 我真的沒有那么深入,但我懷疑這個問題再次發生了變化。 因為它是SpringJUnit4ClassRunner
,它首先統治它們以及上下文層次結構。 另一方面,JUnit測試具有良好定義和管理的生命周期(幾乎是列表servlet容器)。
任何理解內部運作的方式我相信你應該很容易解決這個問題:)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.