在没有异步调用@Async的情况下注释的方法,在应用程序上下文启动之前收到ContextRefreshedEvent

时间:2018-06-13 13:49:30

标签: java spring spring-boot

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

场景1

创建一个类BlockingListener的单例并在bean工厂注册。这是在另一个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);
        }

    }

}

预期的行为是第二​​个场景,因为ContextRefreshedEvent应该在上下文成功启动后发布。但是,我无法弄清楚为什么动态注册bean的bean在上下文启动之前收到了事件,以及为什么它阻止了上下文的启动。

1 个答案:

答案 0 :(得分:2)

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

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

您在给定设置中可以执行的操作是将后处理器手动应用为:

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