如何使用Spring正确关闭执行器服务?

我有一个命令行应用程序,它使用Spring管理的Bean,它由以下java创建:ExecutorService

ExecutorService service = Executors.newFixedThreadPool(4);

现在,我希望我的服务在我的应用程序关闭时关闭,所以我让我的bean实现接口,并有一个销毁方法,例如:DisposableBean

public void destroy(){
  service.shutdown();
}

然后,我可能会想做一些事情,比如在Spring上下文中注册一个关闭钩子。然而,我发现(困难的方式,即在预生产版本中)这不起作用:在调用方法之前不会调用关闭钩子,从而导致经典的 catch 22 问题(它确实在中断时被调用,即,如果我在应用程序运行时按 Ctrl-C)。这逃脱了我的单元测试,因为出于某种原因,它似乎在JUnit内部工作正常,这仍然让我感到困惑:JUnit做了什么不同的事情?ExecutorService.shutdown()

到目前为止,我发现的解决方案是在退出main函数之前显式调用。我想知道是否有更好的解决方案,以及由Spring管理灵活线程池的最佳实践是什么。另外,如果我的豆子不是由Spring直接管理的,而是由Spring管理的豆子创建的,该怎么办?我应该将调用级联到吗?这难道不是很容易出错吗?ApplicationContext.close()destroy()

我感谢任何评论,建议,进一步阅读,RTFM,魔术食谱。

谢谢!


答案 1

您是否知道:

ExecutorService service = Executors.newFixedThreadPool(4);

可以替换为:

<bean id="service" class="java.util.concurrent.Executors" 
      factory-method="newFixedThreadPool" destroy-method="shutdown">
    <constructor-arg value="4"/>
</bean>

然后,spring上下文更直接地管理执行器服务的关闭 - 并且可以更轻松地重用它。


答案 2

根据官方的Spring文档,当使用基于注释的配置时,对于 destroyMethod 字段,Spring 的默认行为是自动调用公共的、无 arg 的方法命名或当应用程序上下文被关闭时。@Beancloseshutdown

为了方便用户,容器将尝试针对从@Bean方法返回的对象推断销毁方法。例如,给定一个返回Apache Commons DBCP BasicDataSource的@Bean方法,容器将注意到该对象上可用的close()方法,并自动将其注册为销毁方法。这种“销毁方法推断”目前仅限于检测名为“close”或“shutdown”的公共无参数方法。该方法可以在继承层次结构的任何级别声明,并且无论@Bean方法的返回类型如何(即,在创建时对Bean实例本身进行反射性检测),都会被检测到。

要进行重复迭代,这是未显式设置销毁方法时注释驱动配置的默认行为。如果不希望此行为显式设置 destroy 方法为空字符串,将禁用此“功能”:

要禁用特定@Bean的销毁方法推理,请指定一个空字符串作为值,例如 @Bean(destroyMethod=“”)。请注意,DisposableBean 和 Closeable/AutoCloseable 接口仍将被检测到,并调用相应的 destroy/close 方法。

另一方面,使用 XML 配置时,这不是默认行为...要实现奇偶校验,可以将销毁方法显式设置为 。有关详细信息,请参阅官方文档中的销毁回调默认初始化和销毁方法部分。(inferred)