Loading

Spring ApplicationListener分析

  Spring 的核心是 ApplicationContext,它负责管理 Bean的完整生命周期;当加载 Bean 时,ApplicationContext 发布某些类型的事件;例如,当上下文启动时,ContextStartedEvent 发布消息,当上下文停止时,ContextStoppedEvent 发布消息;

  通过 ApplicationEvent 类和 ApplicationListener 接口来提供在 ApplicationContext 中处理事件;如果一个 Bean 实现 ApplicationListener 接口,那么每次 ApplicationEvent 被发布到 ApplicationContext 上,那个 Bean实例会被通知;类似MQ的发布订阅;

   

  ApplicationEvent UML图如下:

  

 

  • Spring常用事件

序号 Spring 事件
1 ContextRefreshedEvent:ApplicationContext 被初始化或刷新时,该事件被发布;这也可以在 ConfigurableApplicationContext 接口中使用 refresh() 方法来发生;容器刷新完成(所有bean都完全创建)会发布这个事件;
2 ContextStartedEvent:当使用 ConfigurableApplicationContext 接口中的 start() 方法启动 ApplicationContext 时,该事件被发布;
3 ContextStoppedEvent:当使用 ConfigurableApplicationContext 接口中的 stop() 方法停止 ApplicationContext 时,发布这个事件;
4 ContextClosedEvent:当使用 ConfigurableApplicationContext 接口中的 close() 方法关闭 ApplicationContext 时,该事件被发布;
5 RequestHandledEvent:在Web应用中,当一个http请求结束触发该事件

 

  • org.springframework.context.support.AbstractApplicationContext#refresh

  

  AbstractApplicationContext#initApplicationEventMulticaster 用于初始化事件多播器;

  AbstractApplicationContext#registerListeners 用于将事件监听器注册到多播器;

 

  • AbstractApplicationContext#initApplicationEventMulticaster

  ApplicationEventMulticaster该接口定义了具体事件监听器的注册管理以及事件发布的方法 ;

  

 

  

  ApplicationEventMulticaster有一抽象实现类AbstractApplicationEventMulticaster,它实现了事件监听器的管理 功能;出于灵活性和扩展性考虑,事件的发布功能则委托给了其子类,即ApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)

  SimpleApplicationEventMulticaster 是 Spring 提 供 的 AbstractApplicationEventMulticaster的一个子类实现,添加了事件发布功能的实现;

 

  • AbstractApplicationContext#registerListeners

  

  这里画了三个大框,分别做不同的事情;

    • 第一个大框,里面处理的是将容器中ApplicationListener的实现类注册到多播器中;
    • 第二大个框,里面处理的是将自定义的ApplicationListenr的实现类注册到多播器中;

      DefaultListableBeanFactory#doGetBeanNamesForType

      自定义ApplicationListener的实现类最终会在这里组装到List集合;

      里面调用的AbstractBeanFactory#isTypeMatch,用于判断要查询的beanName的实例与该匹配的类型是否一致;

  

  •  
    • 第三个大框,earlyApplicationEvents是早期事件,而早期事件是指多播器未创建完成前的事件,也就AbstractApplicationContext#initApplicationEventMulticaster方法执行前,将没有多播器则将之前的事件保存,之后调用getApplicationEventMulticaster().multicastEvent(earlyEvent)方法发布事件;

 

  • SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)

  

  getTaskExecutor(),多播器中是否支持线程池异步发送事件;

 

  • SimpleApplicationEventMulticaster#invokeListener

  调用SimpleApplicationEventMulticaster#doInvokeListener

  SimpleApplicationEventMulticaster#doInvokeListener

  

  listener.onApplicationEvent(event),调用对于listener的onApplicationEvent事件;

  Spring的ApplicationContext容器内的事件发布机制,主要用于单一容器内的简单消息通知和处理,并不适合分布式、多进程、多容器之间的事件通知 ;

 

  可以通过如下两种方式为我们的业务对象注入ApplicationEventPublisher的依赖

  • 使用ApplicationEventPublisherAware接口;在ApplicationContext类型的容器启动时,会自动识别该类型的bean定义并将ApplicationContext容器本身作为ApplicationEventPublisher注入当前对象,而ApplicationContext容器本身就是一个ApplicationEventPublisher;

  • 使用ApplicationContextAware接口; ApplicationContext本身就是一个ApplicationEventPublisher,通过ApplicationContextAware几乎达到第一种方式相同的效果;

 

  • ApplicationListener,ApplicationEvent,ApplicationEventMulticaster,ApplicationEventPublisher类关系图

  

 

  Spring的事件监听是基于观察者模式的,有三部分组成:

    • 事件(ApplicationEvent):负责对应相应监听器,事件源发生某事件是特定事件监听器被触发的原因;
    • 监听器(ApplicationListener):对应于观察者模式中的观察者,监听器监听特定的事件,并在内部定义了事件后发生相应的逻辑;
    • 事件发布器(ApplicationEventMulticaster):对应于观察者模式中的被观察者/主题,负责通知观察者对外提供发布事件和增删事件监听器的接口,维护事件和监听器的映射关系,并在事件发生时负责通知相关的监听器;

 

  Spring事件机制是观察者模式的一种实现,但是除了发布者和监听者两个角色外,还有一个EventMultiCaster(事件多播器)的角色负责把事件转发给监听器,在代码中体现:发布者调用applicationEventPublisher.publishEvent(msg); 将事件发送给了EventMultiCaster,之后由EventMultiCaster注册所有的Listener,然后根据事件类型决定转发给哪个Listener

 

  测试代码如下:

  配置类

@Configuration
@Import (value = { DemoApplicationListener1. class })
public class ListenerConfig1 {

}

  

  事件监听类

查看代码

public class DemoApplicationListener1  implements ApplicationListener {
    private final static Logger logger = LoggerFactory
            .getLogger(DemoApplicationListener1. class );

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        logger.info( "接收到一个事件:" + event);
    }
}

  

  测试类

查看代码
 @Test
public void listenerTest1() {
    AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(
            ListenerConfig1. class );

    context.publishEvent( new ApplicationEvent( "发送测试消息" ) {
    });


    try {
        TimeUnit.SECONDS.sleep( 5 );
    }
    catch (InterruptedException e) {
        e.printStackTrace();
    }

    context.close();
}

  运行结果如下:

  从日志可以看出,事件监听处理是在主线程,上面说到的多播器可以支持线程池异步发送事件,在配置类直接的@Bean一个线程池对象是行不通的,这里的对象是直接new出来的,它不能够直接给Spring管理;多播器要支持异步具体有下面两种方式;

  参考:[https://docs.spring.io/spring-framework/docs/5.2.4.RELEASE/spring-framework-reference/core.html#context-functionality-events]

 

  • 方式一

  在AbstractApplicationContext#initApplicationEventMulticaster会判断IOC容器中是否存在beanName为APPLICATION_EVENT_MULTICASTER_BEAN_NAME的多播器对象,即applicationEventMulticaster,因此可以自定义一个多播器并将它的beanName设置为applicationEventMulticaster

  自定义的事件多播器

查看代码
 @Component ( "applicationEventMulticaster" )
public class DemoEventMulticaster  extends SimpleApplicationEventMulticaster {

    public DemoEventMulticaster() {
        /**
         * @See org.springframework.context.support.AbstractApplicationContext#initApplicationEventMulticaster()
         */

        setTaskExecutor(Executors.newCachedThreadPool());
    }
}

  

  上面的配置类,@Import部分修改如下:

@Import (value = { DemoApplicationListener1. class , DemoEventMulticaster. class })

  

  运行结果如下:

 

  • 方式二

  使用Spring管理的线程池支持事件多播器的异步;

 

  事件监听器

查看代码
 @Component
public class DemoApplicationListener2  implements ApplicationListener  {
    private final static Logger logger = LoggerFactory
            .getLogger(DemoApplicationListener2. class );


    @Override
    @Async
    public void onApplicationEvent(ApplicationEvent event) {
        logger.info( "接收到一个事件:" + event);
    }
}

   

  配置类

查看代码
 @EnableAsync
@Configuration
@Import (value = { DemoApplicationListener2. class })
public class ListenerConfig2  implements AsyncConfigurer {
    private final static Logger logger = LoggerFactory.getLogger(ListenerConfig2. class );

    // 获取线程池
    @Override
    public Executor getAsyncExecutor() {
        // 定义线程池
        ThreadPoolTaskExecutor executor =  new ThreadPoolTaskExecutor();
        // SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();

        // 设置核心线程数
        executor.setCorePoolSize( 4 );

        // 设置线程池最大线程数
        executor.setMaxPoolSize( 10 );

        // 设置线程池队列容量
        executor.setQueueCapacity( 1000 );

        // 初始化
        executor.initialize();

        logger.info(String.valueOf(executor));
        return executor;
    }

}

  

  测试类:

查看代码
 @Test
public void listenerTest2() {
    AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(
            ListenerConfig2. class );

    context.publishEvent( new ApplicationEvent( "发送测试消息" ) {
    });

    try {
        TimeUnit.SECONDS.sleep( 5 );
    }
    catch (InterruptedException e) {
        e.printStackTrace();
    }

    context.close();
}

  

  运行结果如下:

 

  也可以使用@EventListener事件注解;

  修改上面的事件监听器

查看代码
 @Component
public class DemoApplicationListener2 {
    private final static Logger logger = LoggerFactory
            .getLogger(DemoApplicationListener2. class );


    @Async
    @EventListener
    public void onApplicationEvent(ApplicationEvent event) {
        logger.info( "接收到一个事件:" + event);
    }
}

 

  运行结果如下:

  参考:[https://docs.spring.io/spring-framework/docs/5.2.4.RELEASE/spring-framework-reference/core.html#context-functionality-events-annotation]

  

posted @ 2020-01-25 00:37  街头卖艺的肖邦  阅读(439)  评论(0编辑  收藏  举报