正在并行执行 Spring 初始化 Bean
Executing Spring Initializing Beans in Parallel
我有多个 Spring InitializingBean
class,我希望它们能并行发送给所有 运行 afterPropertiesSet()
。然而,当我运行一个小例子时,它们是同步执行的。有没有办法并行执行它们?
下面是一个初始化 bean 的示例,可用于测试我所指的内容。当像这样创建多个 class 时(即 InitBeanOne
、InitBeanTwo
、...),日志显示它们正在 运行 同步。
我想到的一个想法是让一个初始化 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();
}
}
}
我有多个 Spring InitializingBean
class,我希望它们能并行发送给所有 运行 afterPropertiesSet()
。然而,当我运行一个小例子时,它们是同步执行的。有没有办法并行执行它们?
下面是一个初始化 bean 的示例,可用于测试我所指的内容。当像这样创建多个 class 时(即 InitBeanOne
、InitBeanTwo
、...),日志显示它们正在 运行 同步。
我想到的一个想法是让一个初始化 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();
}
}
}