使用@Async注释的方法不会异步调用,并且在应用程序上下文启动之前接收 ContextRefreshedEvent



我提出了两种场景,一种是使用bean工厂注册的bean,另一种是通过自动扫描包以查找注释定义(例如@Component(创建的bean。Bean 类使用用@EventListener@Async注释的方法侦听ContextRefreshedEvent,以便异步调用它。

场景 1

创建类BlockingListener的单一实例并将其注册到豆工厂。这是在初始化另一个 Bean 时完成的,如下文方法afterPropertiesSet中所述。收到ContextRefreshedEvent但不退出,因此应用程序不会启动。它仍然被封锁。

@EnableAsync
@EnableScheduling
@EnableAutoConfiguration
@SpringBootApplication
public class SampleApp implements InitializingBean {
private final DefaultListableBeanFactory beanFactory;
@Autowired
public SampleApp(DefaultListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public void afterPropertiesSet() {
String beanName = "blocking-listener";
Object bean = new BlockingListener();
beanFactory.registerBeanDefinition(beanName, rootBeanDefinition(bean.getClass()).getBeanDefinition());
beanFactory.registerSingleton(beanName, bean);
}
public static void main(final String... args) {
SpringApplication.run(SampleApp.class, args);
}
public static class BlockingListener {
@Async
@EventListener(ContextRefreshedEvent.class)
void block() throws InterruptedException {
Thread.sleep(Long.MAX_VALUE);
}
}
}

场景 2

BlockingListener@Component注释,并自动检测和创建 bean。收到ContextRefreshedEvent但不退出,但应用程序启动。

@EnableAsync
@EnableScheduling
@EnableAutoConfiguration
@SpringBootApplication
public class SampleApp  {
private final DefaultListableBeanFactory beanFactory;
@Autowired
public SampleApp(DefaultListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public static void main(final String... args) {
SpringApplication.run(SampleApp.class, args);
}
@Component
public static class BlockingListener {
@Async
@EventListener(ContextRefreshedEvent.class)
void block() throws InterruptedException {
Thread.sleep(Long.MAX_VALUE);
}
}
}

预期行为与第二种方案相同,因为上下文刷新事件应在上下文成功启动后发布。但是,我无法弄清楚为什么在 bean 工厂动态注册的 bean 在上下文启动之前收到事件,以及为什么它会阻止上下文启动。

在方案 1 中,block()的调用不会异步发生,因为批注@Async不会生效。

@Async正在通过一个BeanPostProcessor,即用代理包装实例的AsyncAnnotationBeanPostProcessor。但是,当您手动将 Bean 添加到 Bean 工厂时,不会应用后处理器。

在给定设置中,您可以执行的操作是手动应用后处理器,如下所示:

bean = beanFactory.initializeBean(bean, beanName);
beanFactory.registerSingleton(beanName, bean);

最新更新