我有一个命令行应用程序,它使用 Spring 管理的 bean,该 bean 由使用以下命令创建的 java
ExecutorService
组成:
ExecutorService service = Executors.newFixedThreadPool(4);
现在,我希望我的服务在我的应用程序关闭时关闭,所以我让我的 bean 实现
DisposableBean
接口并具有一个销毁方法,例如:
public void destroy(){
service.shutdown();
}
然后我可能会想做一些事情,比如在 Spring 上下文中注册一个关闭钩子。然而我发现(艰难的方式,即在预生产版本中)这不起作用:在调用
ExecutorService.shutdown()
方法之前不会调用关闭挂钩,导致经典的 catch 22 问题(它确实会在中断时被调用,即,如果我在应用程序运行时按 Ctrl-C)。这逃脱了我的单元测试,因为出于某种原因,它似乎在 JUnit 中工作得很好,这仍然让我感到困惑:JUnit 的做法有何不同?
到目前为止我找到的解决方案是在退出主函数之前显式调用
ApplicationContext.close()
。我想知道是否有更好的解决方案,以及由 Spring 管理灵活线程池的最佳实践是什么。另外,如果我的 bean not 由 Spring 直接管理,而是由 Spring 管理的 bean 创建,该怎么办?我应该将呼叫级联到destroy()
吗?这不是很容易出错吗?
我感谢任何评论、建议、进一步阅读、RTFM、神奇食谱。
谢谢!
你知道吗:
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 上下文会更直接地管理执行器服务的关闭——并且可以更轻松地重用它。
根据 Spring 官方文档,当使用基于注释的配置时,对于 @Bean
的
destroyMethod字段,Spring 的默认行为是在应用程序上下文正在运行时自动调用名为
close
或 shutdown
的公共、无参数方法。关闭。
为了方便用户,容器将尝试推断 destroy 方法针对从 @Bean 方法返回的对象。为了 例如,给定一个返回 Apache Commons DBCP 的 @Bean 方法 BasicDataSource,容器会注意到close()方法 在该对象上可用并自动将其注册为 销毁方法。这种“破坏方法推断”目前仅限于 仅检测名为“close”或“shutdown”的公共、无参数方法。这 方法可以在继承层次结构的任何级别声明,并且 无论 @Bean 方法的返回类型如何,都会被检测到 (即,检测是针对 bean 实例本身反射发生的 在创建时)。
重申一下,当未显式设置销毁方法时,这是“注释驱动”配置的默认行为。如果不需要这种行为,则显式将 destroy 方法设置为空字符串将禁用此“功能”:
要禁用特定@Bean的销毁方法推断,请指定一个 空字符串作为值,例如@Bean(destroyMethod="")。请注意, DisposableBean 和 Closeable/AutoCloseable 接口将 尽管如此,还是会被检测到并使用相应的销毁/关闭方法 被调用。不是另一方面,当使用 XML 配置时,这
默认行为...为了实现奇偶校验,destroy-method 可以显式设置为 (inferred)
。有关详细信息,请参阅官方文档中的
销毁回调和默认初始化和销毁方法部分。
@PreDestroy
注释:
@PreDestroy
@SneakyThrows
void shutdownExecutor() {
executor.shutdown();
if (!executor.awaitTermination(1, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
}