如何阻止Spring应用程序上下文关闭,直到关闭挂钩为止

时间:2016-06-10 17:50:23

标签: java spring asynchronous spring-boot

我有一个spring-boot应用程序。

我在我的bean中实现了SmartLifecycle接口,它在start方法中启动async snmp服务器,并以stop方法停止它。

一切正常,除了主应用程序上下文在启动后立即停止的事实,所以我的服务器bean也在启动后立即停止。

我只需要在关闭挂钩时停止弹簧上下文。

这不是一个Web应用程序,因此我不需要spring-boot-starter-web,这可以通过启动webserver来解决此问题,该服务器会阻止上下文停止,直到Web服务器停止。

我可以使用类似CountDownLatch之类的内容,并在上下文启动后立即在main方法中等待它为零。 Somethig喜欢这样:

public static void main(String[] args) throws InterruptedException {
    ConfigurableApplicationContext ctx = SpringApplication.run(SnmpTrapRetranslatorApplication.class, args);
    CountDownLatch snmpServerCloseLatch = ctx.getBean("snmpServerCloseLatch", CountDownLatch.class);
    snmpServerCloseLatch.await();
}

我的服务器bean的start方法将使用count 1创建此锁存器,而stop方法将调用snmpServerCloseLatch.countDown()

此技术描述为here

但是错误的是我的main方法负责等待我的自定义服务器bean停止。我觉得这不对。

例如spring-boot-starter-web如何做到这一点?当它启动tomcat时,它会一直运行,直到收到shutdown hook并且不需要main方法中的任何管理代码。它仅在上下文接收到shoutdown信号时停止。

例如,当我在bean中使用@Scheduled方法时,同样的行为。 Spring也不会自动停止上下文。仅限于CTRL-C

我想达到类似的效果。我的main方法应该只有一行:启动上下文。上下文应该在启动或停止时启动和停止异步服务器(已由SmartLifecycle实现),并且在请求关闭之前不应该停止(CTRL-C,SIGINT等)。

2 个答案:

答案 0 :(得分:3)

我的调查引导我解决问题的核心:daemon threads

我使用的snmp服务器实现(snmp4j)在内部使用守护程序线程。因此,即使snmp服务器启动,JVM中也没有更多的实时用户线程,因此它会退出。

<强> TL / DR

只需将此方法添加到任何bean(snmp服务器bean是这个的好选择):

@Scheduled(fixedDelay = 1000 * 60 * 60) // every hour
public void doNothing() {
    // Forces Spring Scheduling managing thread to start
}

(不要忘记在弹簧配置中添加@EnableScheduling)。

<强>解释

为防止停止弹簧上下文,当SNMP服务器仍在运行时,我们需要任何非守护程序线程在JVM中处于活动状态。不一定是main线程。所以我们可以让main方法完成。

  1. 我们可以从我们的服务器bean的start方法运行新的非守护程序线程。该帖子会wait对某些while变量进行running循环检查,而我们的stop方法会将此running变量设置为false这个锁上的{和} notifyAll

    这样,我们的非守护程序线程将一直处于活动状态,直到触发了击倒钩子(并阻止JVM退出)。 在关闭钩子之后,spring上下文生命周期close方法将调用所有SmartLifecycle bean的close方法,这将导致SNMP服务器bean的stop方法调用,这将导致将running设置为false,这将导致我们的非守护程序线程停止,从而允许JVM正常停止。

  2. 或者我们可以以类似的方式使用Spring的调度线程。它也是非守护程序线程,因此它将阻止JVM退出。 Spring自己管理这个线程,所以当触发关闭钩子时它会自动停止它。

    要使Spring的调度线程启动,我们需要在任何bean中使用任何@Scheduled方法。

  3. 我认为第一种(手动)方法仍然更多&#34;正确&#34;,同时需要更多异步编码(这是我们都知道的容易出错)。谁知道Spring将如何改变它未来的日程安排实施。

答案 1 :(得分:1)

    SpringApplication app = new SpringApplication(Main.class);
    app.setRegisterShutdownHook(false);
    ConfigurableApplicationContext applicationContext= app.run();
    Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
        @Override
        public void run() {
            //do your things
            applicationContext.close();
        }
    }));