【问题标题】:Delegate processing during Tomcat startup with Spring使用 Spring 在 Tomcat 启动期间委托处理
【发布时间】:2015-09-26 11:57:32
【问题描述】:

我已经定义了一个 bean,它需要在 @PostConstruct 生命周期阶段(在启动期间)进行一些繁重的处理。

就目前而言,我在处理循环的每次迭代中向执行器服务提交一个新的 Callable。我将这些提交返回的 Future 对象列表保存在成员变量中。

@Component
@Scope("singleton")
public class StartupManager implements ApplicationListener<ContextRefreshedEvent> {

    @Autowired
    private ExecutorService executorService;

    private final Map<Class<?>, Optional<Action>> actionMappings = new ConcurrentHashMap<>();
    private final List<Future> processingTasks = Collections.synchronizedList(new ArrayList<>());

    @PostConstruct
    public void init() throws ExecutionException, InterruptedException {

        this.controllers.getHandlerMethods().entrySet().stream().forEach(handlerItem -> {

            processingTasks.add(executorService.submit(() -> {

                // processing

            }));

        });

    }

}

同样的 bean 实现了 ApplicationListener 接口,因此它可以侦听 ContextRefreshedEvent,它允许我检测应用程序何时完成启动。我使用这个处理程序来遍历 Futures 列表并调用阻塞的 get 方法,以确保在应用程序继续之前所有的处理都已经发生。

@Override
public void onApplicationEvent(ContextRefreshedEvent  applicationEvent) {
    for(Future task : this.processingTasks) {
        try {
            task.get();
        } catch (InterruptedException | ExecutionException e) {
            throw new IllegalStateException(e.getMessage());
        }
    }
}

我的第一个问题... 将 actionMapping 流更改为 parallelStream 是否与将任务提交给执行器服务相同?有没有办法可以将现有的执行程序服务传递到并行流中,以使用我为 bean 定义的线程池大小?

其次.. 作为处理的一部分.. 读取 actionMappings 映射并将条目放入其中。在这种情况下,让这个 Map 成为 ConcurrentHashMap 以使其线程安全就足够了吗?

其次是实现 ApplicationListener 接口并侦听 ContextRefreshedEvent 是检测应用程序何时启动并因此通过阻塞强制完成未处理任务的最佳方法吗?或者这可以通过其他方式完成吗?

谢谢。

【问题讨论】:

    标签: java multithreading spring tomcat


    【解决方案1】:
    1. 关于使用parallelStream():不,这正是使用这种方法的主要缺点。它应该只在线程池大小无关紧要时使用,所以我认为你的基于ExecutorService 的方法很好。

      由于您使用的是 Java 8,因此您也可以使用 CompletableFuture.supplyAsync() 方法,该方法具有采用 Executor 的重载。由于ExecutorService 扩展了Executor,您可以将ExecutorService 传递给它,您就完成了!

    2. 我认为ConcurrentHashMap 很好。它确保所有操作中的线程安全,尤其是在添加或修改条目时。

    3. ContextRefreshedEvent 何时被解雇?根据Javadoc:

      ApplicationContext 被初始化或刷新时引发的事件。

      这不能保证您的 onApplicationEvent() 方法只被调用一次,也就是说,当您的 bean 正确初始化时,包括执行 @PostConstruct-annotated 方法。

      我建议你实现BeanPostProcessor 接口并将Future-checkup 逻辑放在postProcessAfterInitialization() 方法中。两个BeanPostProcessor方法分别在InitializingBean.afterPropertiesSet()方法(如果存在)之前和之后调用。

    我希望这会有所帮助...

    干杯,

    杰夫

    【讨论】:

    • 感谢杰夫的回答,您的观点非常简洁明了。关于 BeanPostProcessor 的一个问题。据我所知,在初始化后,每个 bean 都会调用 postProcessAfterInitialization() 方法。我正在关注一个在上下文中初始化所有 bean 时引发的事件。这允许处理任务在发出阻塞调用之前有更多时间自然完成。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-10
    • 1970-01-01
    • 1970-01-01
    • 2012-04-13
    • 1970-01-01
    相关资源
    最近更新 更多