12

我有一个命令行应用程序,它使用 Spring 管理的 bean,该 bean 由ExecutorService创建的 java 组成:

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不是由 Spring 直接管理而是由 Spring 管理的 bean 创建的怎么办?我应该将调用级联到destroy()吗?这不是很容易出错吗?

我感谢任何意见、建议、进一步阅读、RTFM、魔术食谱。

谢谢!

4

4 回答 4

28

您是否知道这一点:

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 上下文更直接地管理 executor 服务的关闭——它可以更容易地重用。

于 2013-08-29T16:28:10.887 回答
8

根据 Spring 官方文档,当使用基于注解的配置时,对于 的destroyMethod字段@Bean,Spring 的默认行为是自动调用 public、no-arg 方法命名closeshutdown当应用程序上下文被关闭时。

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

重申一下,当没有显式设置销毁方法时,这是注释驱动配置的默认行为。如果不希望出现这种行为,将 destroy 方法显式设置为空字符串将禁用此“功能”:

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

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

于 2016-12-01T18:52:43.293 回答
1

考虑使用 Spring 的 TaskExecutor,可以配置线程池。 http://static.springsource.org/spring/docs/3.0.x/reference/scheduling.html

于 2013-08-29T17:09:27.950 回答
0

只想将基于配置的 ExecutorService bean 创建添加到@Keith 的答案

@Bean("fixedThreadPool")
public ExecutorService fixedThreadPool() {
    return Executors.newFixedThreadPool(THREAD_POOL_SIZE);
}
于 2021-04-15T16:17:57.790 回答