junit 如何使用Spring正确关闭executor服务?

qij5mzcb  于 12个月前  发布在  Spring
关注(0)|答案(5)|浏览(171)

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

ExecutorService service = Executors.newFixedThreadPool(4);

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

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


然后我可能会尝试在Spring上下文中注册一个shutdown hook。(硬的方式,即在预生产版本),这不工作:在调用ExecutorService.shutdown()方法之前,shutdown钩子不会被调用,这会导致经典的catch 22问题(它确实会在中断时被调用,也就是说,如果我在应用程序运行时按下Ctrl-C)。这逃过了我的单元测试,因为出于某种原因,它似乎在JUnit内部工作得很好,这仍然让我感到困惑:JUnit做什么有所不同?
到目前为止,我找到的解决方案是在退出main函数之前显式调用ApplicationContext.close()。我想知道是否有更好的解决方案,以及由Spring管理灵活线程池的最佳实践是什么。还有,如果我的bean不是由Spring直接管理的,而是由Spring管理的bean创建的,那该怎么办?我应该级联调用destroy()吗?这不是很容易出错吗?
我感谢任何意见,建议,进一步阅读,RTFM,神奇食谱。
谢谢你,谢谢

5q4ezhmt

5q4ezhmt1#

你是否意识到:

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服务的关闭--并且可以更容易地重用它。

bxfogqkk

bxfogqkk2#

根据官方Spring文档,当使用基于注解的配置时,对于@Bean的destroyMethod字段,Spring的默认行为是在应用程序上下文关闭时自动调用名为closeshutdown的公共无参数方法。
为了方便用户,容器将尝试根据从@Bean方法返回的对象推断destroy方法。例如,给定返回Apache Commons DBCP BasicDataSource的@Bean方法,容器将注意到该对象上可用的close()方法并自动将其注册为destroyMethod。这种“destroy方法推断”目前仅限于检测public,名为'close'或'shutdown'的no-arg方法。该方法可以在继承层次结构的任何级别声明,并且无论@Bean方法的返回类型如何都将被检测到(即,在创建时对Bean示例本身进行反射检测)。
重新定义,这是 annotation-driven 配置在没有显式设置destroy方法时的默认行为。如果不需要此行为,显式设置destroy方法为空字符串将禁用此“功能”:
要禁用特定@Bean的destroy方法推断,请指定一个空字符串作为值,例如@Bean(destroyMethod="”)。请注意,DisposableBean和Closeable/AutoCloseable接口仍然会被检测到,并调用相应的destroy/close方法。
另一方面,当使用XML配置时,这不是默认行为.为了实现奇偶校验,destroy-method可以显式设置为(inferred)。有关详细信息,请参阅官方文档中的Destruction回调和默认初始化以及destroy方法部分。

osh3o9ms

osh3o9ms4#

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

@Bean("fixedThreadPool")
public ExecutorService fixedThreadPool() {
    return Executors.newFixedThreadPool(THREAD_POOL_SIZE);
}

字符串

oyxsuwqo

oyxsuwqo5#

也可以使用@PreDestroy注解:

@Component
public class MyService {
  private final ExecutorService executor = Executors.newCachedThreadPool();

  @PreDestroy
  @SneakyThrows
  void shutdownExecutor() {
    executor.shutdown();
    if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
      executor.shutdownNow();
    }
  }
}

字符串
或者,如果ExecutorService本身是一个bean:

@Bean(name = "fixedThreadPool", destroyMethod = "shutdown")
public ExecutorService fixedThreadPool() {
    return Executors.newFixedThreadPool(THREAD_POOL_SIZE);
}

相关问题