springboot listeners 事件监听机制 @EventListener详解

本人博客原地址:springboot listeners 事件监听机制 @EventListener详解
创作时间: 2019.06.11 16:53:43

基于springboot2.1.4

springboot在刚启动的时候构造SpringApplication对象就会加载一系列已配置的listeners(详细介绍),此处的listeners实现的是org.springframework.context.ApplicationListener接口,准确来说是org.springframework.context.event.SmartApplicationListener,该接口提供检查支持事件类型的接口方法,支持排序

在整个应用的启动过程中,springboot会发布一系列的event,不同的listener在实现接口时,定义支持的event类型。

首先在org.springframework.boot.SpringApplication#run(java.lang.String…)中加载SpringApplicationRunListenerspringboot listeners 事件监听机制 @EventListener详解_第1张图片

需要注意的是,此处是实现org.springframework.boot.SpringApplicationRunListener接口的listener,跟构造SpringApplication对象就会加载一系列已配置的listeners不同

先来看看org.springframework.boot.SpringApplicationRunListenersspringboot listeners 事件监听机制 @EventListener详解_第2张图片
在应用启动过程中会在不同的阶段调用不同的方法,发布不同的事件,默认情况下,listeners的list中只有org.springframework.boot.context.event.EventPublishingRunListener的实例。
以org.springframework.boot.SpringApplicationRunListeners#environmentPrepared为例:
后面就是调用的org.springframework.boot.context.event.EventPublishingRunListener#environmentPreparedspringboot listeners 事件监听机制 @EventListener详解_第3张图片
从SpringApplicationRunListeners到现在,其实都在干的是发布事件的事,SpringApplicationRunListeners和EventPublishingRunListener都不应冠于Listener的字眼,他们应该叫publisher。

接下来看如何调用真正的Listener监听事件。
先看下上图中的如何获取发布某个事件时,获取到那些具体的listenerspringboot listeners 事件监听机制 @EventListener详解_第4张图片
如图getApplicationListeners–>org.springframework.context.event.AbstractApplicationEventMulticaster#retrieveApplicationListeners执行的时候,会根据listener实现的supportsSourceType方法判断listener支持的事件类型,再根据事件类型存到不同的list中。
获取到支持某个事件的listeners之后,就是遍历listeners执行onApplicationEvent方法了
org.springframework.context.event.SimpleApplicationEventMulticaster#invokeListener->org.springframework.context.event.SimpleApplicationEventMulticaster#doInvokeListener
到这,listener的处理就算完成了

另外当有自定义listener,通常会使用@EventListener注解再监听的方法上,实际上是通过EventListenerMethodProcessor,解析配置类包含@EventListener注解的方法,通过org.springframework.context.event.DefaultEventListenerFactory将对应的方法包装成org.springframework.context.event.ApplicationListenerMethodAdapter#ApplicationListenerMethodAdapter对象,改对象实际上就是一个listener

解析时机:

在所有非懒加载的bean都实例化之后执行org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons----buguo>org.springframework.context.event.EventListenerMethodProcessor#afterSingletonsInstantiated—>org.springframework.context.event.EventListenerMethodProcessor#processBeanspringboot listeners 事件监听机制 @EventListener详解_第5张图片
在afterSingletonsInstantiated遍历beanName时,beanName是通过已定义的bean列表获取,而非已实例化的,所以不管@lazy注解在类还是方法上都不妨碍EventListenerMethodProcessor对注解@EventListener注解的方法的解析。
发布event与监听到的过程与上面的类似。在org.springframework.context.event.SimpleApplicationEventMulticaster#doInvokeListener发布event后,会调用ApplicationListenerMethodAdapter的org.springframework.context.event.ApplicationListenerMethodAdapter#onApplicationEvent然后会执行被代理的@EventListener方法
—>org.springframework.context.event.ApplicationListenerMethodAdapter#processEvent----->org.springframework.context.event.ApplicationListenerMethodAdapter#doInvoke当监听的方法有@Async注解时,listener会封装成EnhancerBySpringCGLIB代理对象springboot listeners 事件监听机制 @EventListener详解_第6张图片
调用this.method.invoke(bean, args)时会被拦截到——>org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept–>org.springframework.aop.interceptor.AsyncExecutionInterceptor#invokespringboot listeners 事件监听机制 @EventListener详解_第7张图片
具体@Async原理请拉到最后查看

贡献自定义代码如下:

  • 首先定义event
public class DemoEvent extends ApplicationEvent{
    private static final long serialVersionUID = 1L;
    private String msg;

    public DemoEvent(Object source,String msg) {
        super(source);
        this.msg = msg;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

}
  • 定义listener
@Component
//@Lazy不会生效
public class DemoListener implements ApplicationListener{
	
	
	
	/**
	 * 该方法不为ApplicationListener的实现方法,当DemoListener实现了业务接口时,spring默认采用
	 * JDK自带的动态代理方式(需要业务类实现接口), 如果实现类存在不属于接口的方法,则会出现以下异常
	 * Caused by: java.lang.IllegalStateException:
	 *  Need to invoke method 'handleDemoEvent' declared on target class 'DemoListener', 
	 *  but not found in any interface(s) of the exposed proxy type. 
	 *  Either pull the method up to an interface 
	 *  or switch to CGLIB proxies by enforcing proxy-target-class mode in your configuration.
	 *  此时需要将@EnableAsync设置为@EnableAsync(proxyTargetClass=true)
	 * @param event
	 */
	@Async("myExecutor")
    @EventListener //注意此处 此时不用implements ApplicationListener
    public void handleDemoEvent(DemoEvent event){
        System.out.println("handleDemoEvent:我监听到了pulisher发布的message为:"+event.getMsg()+Thread.currentThread().getName());

    }

    @Async("myExecutor1")
	@Override //此时需要implements ApplicationListener
	public void onApplicationEvent(DemoEvent event) {
		 System.out.println("DemoListener: onApplicationEvent监听到了pulisher发布的message为:"+event.getMsg()+Thread.currentThread().getName());
	}
    
    @Async//使用默认的executor SimpleAsyncTaskExecutor
    @EventListener
//@Lazy不会生效
    public void handleDemoEvent1(DemoEvent event){
        System.out.println("handleDemoEvent1:我监听到了pulisher发布的message为:"+event.getMsg()+""+
    Thread.currentThread().getName());

    }

}
在启动类添加
@SpringBootApplication
 * proxy-target-class属性值决定是基于接口的还是基于类的代理被创建。
 * 如果proxy-target-class 属性值被设置为true,那么基于类的代理将起作用
 * (这时需要cglib库)。如果proxy-target-class属值被设置为false或者这个
 * 属性被省略,那么标准的JDK 基于接口的代理
 *
 */
@EnableAsync(proxyTargetClass=true)
添加Executor 
@Bean("myExecutor")
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(25);
        executor.setThreadNamePrefix("myExecutor_");
        executor.initialize();
        return executor;
    }
  • 发布事件
先在需要使用的地方注入context
    @Autowired
    ApplicationContext context;

具体使用的地方
context.publishEvent(new DemoEvent(this, "22"));

关于@EnableAsync(proxyTargetClass=true)和@Async(“myExecutor”)可参考另一篇我看过的文章点击这里或者这里

你可能感兴趣的:(spring源码解读,spring,java,spring,boot)