我提出了两种场景,一种是使用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);