基于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...)中加载SpringApplicationRunListener
需要注意的是,此处是实现org.springframework.boot.SpringApplicationRunListener接口的listener,跟构造SpringApplication对象就会加载一系列已配置的listeners不同
先来看看org.springframework.boot.SpringApplicationRunListeners
在应用启动过程中会在不同的阶段调用不同的方法,发布不同的事件,默认情况下,listeners的list中只有org.springframework.boot.context.event.EventPublishingRunListener的实例。
后面就是调用的org.springframework.boot.context.event.EventPublishingRunListener#environmentPrepared
以org.springframework.boot.SpringApplicationRunListeners#environmentPrepared为例:
从SpringApplicationRunListeners到现在,其实都在干的是发布事件的事,SpringApplicationRunListeners和EventPublishingRunListener都不应冠于Listener的字眼,他们应该叫publisher。
接下来看如何调用真正的Listener监听事件。
先看下上图中的如何获取发布某个事件时,获取到那些具体的listener
如图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#processBean
在afterSingletonsInstantiated遍历beanName时,beanName是通过已定义的bean列表获取,而非已实例化的,所以不管@lazy注解在类还是方法上都不妨碍EventListenerMethodProcessor对注解@EventListener注解的方法的解析。
--->org.springframework.context.event.ApplicationListenerMethodAdapter#processEvent----->org.springframework.context.event.ApplicationListenerMethodAdapter#doInvoke当监听的方法有@Async注解时,listener会封装成EnhancerBySpringCGLIB代理对象
发布event与监听到的过程与上面的类似。在org.springframework.context.event.SimpleApplicationEventMulticaster#doInvokeListener发布event后,会调用ApplicationListenerMethodAdapter的org.springframework.context.event.ApplicationListenerMethodAdapter#onApplicationEvent然后会执行被代理的@EventListener方法
调用this.method.invoke(bean, args)时会被拦截到——>org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept-->org.springframework.aop.interceptor.AsyncExecutionInterceptor#invoke
具体@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<DemoEvent>{
/**
* 该方法不为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<DemoEvent>
public void handleDemoEvent(DemoEvent event){
System.out.println("handleDemoEvent:我监听到了pulisher发布的message为:"+event.getMsg()+Thread.currentThread().getName());
}
@Async("myExecutor1")
@Override //此时需要implements ApplicationListener<DemoEvent>
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")可参考另一篇我看过的文章点击这里或者这里
网友评论