当前位置: 首页 > 知识库问答 >
问题:

Spring上下文层次结构中的豆子销毁顺序

须景辉
2023-03-14

说当Spring上下文层次结构关闭时,没有保证豆子将被销毁的顺序是正确的吗?例如,子上下文中的豆子将在父上下文中销毁。从一个最小的例子来看,上下文的破坏似乎在上下文之间是完全不协调的(奇怪的是)。这两个上下文都注册一个 shutdown hook,稍后将在不同的线程中执行。

@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

有没有办法强制以“正确”的顺序关闭上下文?

共有1个答案

许淳
2023-03-14

我自己也挖掘了同样的问题,一切看起来都不再奇怪了。虽然我仍然希望这表现不同。

当你有父母时

现在可能有一些区别

这种情况最常见的设置(不包括单上下文设置)是通过 DispatcherServlet 使用 ContextLoaderListener 和子上下文声明根上下文。

当webapp(或容器)关闭时,ContextLoaderListenerDispatcherServlet,通过ServletContextListener.contextDestroyed(…)Servlet接收通知。destroy()相应地。

根据javadoc首先servlet

因此,在servlet容器中运行的webapp中,首先销毁DispatcherServlet上下文(它是子1),然后才销毁根上下文。

以下内容不仅适用于独立的WEApp,也适用于使用分层上下文的任何独立的Spring应用。

由于没有容器,因此 stanadlone 应用程序需要与 JVM 本身通信,以便接收关闭信号并对其进行处理。这是使用关机钩子机制完成的。

除了JVM capabilities\version之外,Spring并不试图推断它运行的环境(但是Spring Boot在自动推断env方面做得很好)。

因此,要使Spring注册一个关闭挂钩,您需要在创建上下文(javadoc)时说它这样做。如果您不这样做,您将根本无法调用@PreDestroy/DisposableBean回调。

一旦您向JVM注册了上下文的关机挂钩,它将收到通知,并将正确处理该上下文的关机。

如果您有父子上下文,您可能希望< code >。registerShutdownHook()。这在某些情况下是可行的。但是JVM以不确定的顺序调用关闭挂钩,所以不幸的是,这并没有真正解决主题问题。

那我们该怎么办呢

可能最简单的(尽管不是最优雅的)解决方案是拥有一个< code>ApplicationListener

  • 具有对子上下文的引用
  • 从父上下文中保存对子上下文(例如db连接)至关重要的bean,以便将它们保留到子上下文处于活动状态(这可以使用@Autowire@DependsOn或xml对应项)
  • 在父上下文关闭之前关闭子上下文[s]

最初的问题提出了一个JUnit测试。我真的没有挖那么远,但我怀疑这个又是不同的。因为是SpringJUnit4ClassRunner统治着它们,首先是上下文层次结构。另一方面,JUnit测试有一个定义良好和管理的生命周期(几乎列出servlet容器)。

任何方式已经了解内部工作,我相信它应该很容易为你解决这个:)

 类似资料:
  • 我在web中配置了spring根web上下文。xml文件。我也有几个与此父项相关的子上下文。所有子上下文都是手动创建的: 我想在这个子上下文中管理会话和请求范围的bean。 如何正确创建和配置子上下文,使其能够处理web应用程序范围? 现在我在尝试自动装配会话范围的bean时出现以下错误(显然):

  • 问题内容: 像这样对表格进行排序的最佳方法是什么: 要通过分级排序,它 的ID 或 名字 : “比萨饼” //节点1 “piperoni” //节点1.1 “奶酪” //节点1.2 “额外的奶酪” //节点1.2.1 “vegetariana” //节点1.3 “汉堡” //节点2 ‘咖啡’//节点3 编辑: 名称 末尾的数字是为了更好地可视化strucutre,而不是用于排序。 编辑2: 正如多

  • 我是Spring的新手,如果我做了一些愚蠢的事情,请原谅我。我正在尝试为我的应用程序编写一个使用Spring的集成测试。 我正在创建一个上下文层次结构,如下所示 在我的测试方法中,我试图创建一个新的子上下文,它只有一个bean,它是一个应用程序侦听器,依赖于父方法中的bean。 我面临的问题是,来自子上下文的bean没有收到应用程序事件的通知,而且@Value注释也没有得到处理。 我到底做错了什么

  • 到目前为止,我们一直在使用Spring JpaRepository来满足我们的需求。我觉得这个新需求需要自定义查询,我不认为可以直接使用JParepository来处理这个问题。我只想知道你是怎么想的。不需要自定义sql查询就可以做到这一点吗?

  • 问题内容: 考虑下面的代码 基于我对类成员初始化和构造函数执行顺序的理解。我期望输出是 因为我相信类成员甚至在调用main方法之前就已初始化。但是当我运行程序时,我得到以下输出 我的困惑是,尽管Meal()Lunch()和PortableLunch()在Bread()Cheese()和Lettuce()之前运行,即使它们的构造函数被调用。 问题答案: 这些是实例字段 它们仅在创建实例时存在(执行)

  • 我有这样的层次结构: 因此,用户可以添加他的工作经验。此外,他还可以为特定的项目添加角色。 我想为用户id 1获取项目,但项目之间只有关系 获取用户 获得工作经验 获取角色 获取项目 因此,如果我有更多不同工作经验的角色,我就必须提出20个请求才能得到我的项目。这不是很有效率吗?我必须加载一些不必要的数据。。。 是否可以只创建endpoint:并按用户ID过滤它? 应该如何在API上管理它?对我来