正在并行执行 Spring 初始化 Bean

Executing Spring Initializing Beans in Parallel

我有多个 Spring InitializingBean class,我希望它们能并行发送给所有 运行 afterPropertiesSet()。然而,当我运行一个小例子时,它们是同步执行的。有没有办法并行执行它们?

下面是一个初始化 bean 的示例,可用于测试我所指的内容。当像这样创建多个 class 时(即 InitBeanOneInitBeanTwo、...),日志显示它们正在 运行 同步。

我想到的一个想法是让一个初始化 bean 异步初始化所需的 classes。不过,这是不得已的选择,因为我想单独利用每个 class 的初始化 bean,而不需要其他依赖 classes.

@Component
public class InitBean implements InitializingBean {

    private final static Logger LOGGER = LoggerFactory.getLogger(InitBean.class);

    @Override
    public void afterPropertiesSet() throws Exception {
        LOGGER.info("BEGIN: InitBean");
        TimeUnit.SECONDS.sleep(5);
        LOGGER.info("END: InitBean");
    }
}

您应该将代码重新定位到一个事件监听方法,并用@Async标记该方法。

确保异步功能设置正确。参见:How To Do @Async in Spring.

您应该在 Spring 框架触发 ApplicationReadyEvent 时触发该方法。

@Component
public class InitBean {
    private final static Logger LOGGER = LoggerFactory.getLogger(InitBean.class);

    @Async
    @EventListener
    public void onApplicationReady(ApplicationReadyEvent event) throws Exception {
        LOGGER.info("BEGIN: onApplicationReady");
        TimeUnit.SECONDS.sleep(5);
        LOGGER.info("END: onApplicationReady");
    }
}

警告:通过这样做,其他方法可能会调用before/during该方法的调用。如果该方法执行其他方法所需的任何类型的初始化,则您需要处理它,例如使用 CountDownLatch.


更新

如果您需要应用程序延迟启动序列的完成,直到所有异步方法完成,我认为您需要自己处理。

使用与 InitializingBean 相同的方法创建接口 AsyncInitializingBean,然后创建一个名为 AsyncBeanInitializer@Component 自动连接一个 AsyncInitializingBean[](或 List), 然后让它在 ContextRefreshedEvent.

上使用 ExecutorService 执行所有方法
@Component
public class InitBean implements AsyncInitializingBean { // <== Change interface (only change needed)

    private final static Logger LOGGER = LoggerFactory.getLogger(InitBean.class);

    @Override
    public void afterPropertiesSet() throws Exception {
        LOGGER.info("BEGIN: InitBean");
        TimeUnit.SECONDS.sleep(5);
        LOGGER.info("END: InitBean");
    }
}
public interface AsyncInitializingBean {
    void afterPropertiesSet() throws Exception;
}
@Component
public class AsyncBeanInitializer {
    private final static Logger LOGGER = LoggerFactory.getLogger(AsyncBeanInitializer.class);

    @Autowired(required = false)
    private AsyncInitializingBean[] beans;

    @EventListener
    public void onContextRefreshed(@SuppressWarnings("unused") ContextRefreshedEvent event) throws Exception {
        if (this.beans == null || this.beans.length == 0)
            return;
        ExecutorService executorService = Executors.newWorkStealingPool();
        try {
            AtomicInteger failed = new AtomicInteger();
            for (AsyncInitializingBean bean : beans) {
                executorService.submit(() -> {
                    try {
                        bean.afterPropertiesSet();
                    } catch (Exception e) {
                        failed.incrementAndGet();
                        LOGGER.error("Async afterPropertiesSet() method failed: " + e, e);
                    }
                });
            }
            executorService.shutdown();
            executorService.awaitTermination(60, TimeUnit.MINUTES);
            if (failed.get() != 0)
                throw new RuntimeException(failed.get() + " Async afterPropertiesSet() methods failed. See log for details.");
        } finally {
            executorService.shutdownNow();
        }
    }
}