簡體   English   中英

Spring上下文層次結構中的bean破壞順序

[英]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的設計方式,這適用於所有設置。

現在可能會有一些區別

servlet容器中的Webapp

這種情況下最常見的設置(不計算單上下文設置)是通過DispatcherServlet使用ContextLoaderListener和子上下文聲明根上下文。

當關閉webapp(或容器)時, ContextLoaderListenerDispatcherServlet都會相應地通過ServletContextListener.contextDestroyed(...)Servlet.destroy()接收通知。

根據javadoc,首先servlet和過濾器被破壞,並且只有在它們完成ServletContextListener之后才會被破壞。

因此,在servlet容器中運行的webapps中,首先銷毀DispatcherServlet上下文(它是一個子上下文),然后銷毀根上下文。

獨立的webapp

以下內容不僅適用於獨立的weapps,也適用於任何使用分層上下文的獨立Spring應用程序。

由於沒有容器,因此stanadlone應用程序需要與JVM本身通信才能接收關閉信號並處理它。 這是使用shutdown hooks機制完成的。

除了JVM功能\\版本之外,Spring並沒有嘗試推斷它所運行的環境(但是Spring Boot可以在自動推導env方面做得很好)。

因此,要使Spring注冊一個關閉鈎子,您需要在創建上下文( javadoc )時這樣做。 如果不這樣做,則根本不會調用@PreDestroy / DisposableBean回調。

一旦您向JVM注冊了上下文的關閉掛鈎,它將被通知並將正確處理該上下文的關閉。

如果您有父子環境,則可能需要為每個子環境設置.registerShutdownHook() 這適用於某些情況。 但是JVM以非確定性順序調用了關閉鈎子,所以不幸的是,這並沒有真正解決主題問題。

那我們能做些什么呢?

可能最簡單(盡管不是最優雅)的解決方案是將ApplicationListener<ContextClosedEvent>DisposableBean放在父上下文中,

  • 有參考[s]到兒童背景[s]
  • 從父上下文中保存對子上下文至關重要的bean(例如數據庫連接),以便它們保持到子上下文存活(這可以通過@Autowire@DependsOn或xml對應的方式完成)
  • 在父上下文關閉之前關閉子上下文[s]

JUnit測試

最初的問題提出了一個JUnit測試。 我真的沒有那么深入,但我懷疑這個問題再次發生了變化。 因為它是SpringJUnit4ClassRunner ,它首先統治它們以及上下文層次結構。 另一方面,JUnit測試具有良好定義和管理的生命周期(幾乎是列表servlet容器)。

任何理解內部運作的方式我相信你應該很容易解決這個問題:)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM