本篇文追踪源码详情,吐血输出,建议自己亲自动手追踪
要理解spring事件我们先来说说事件的几个角色概念
下面我们来看看一个简单的demo,当我们下完订单后,我们需要短信通知客户订单下单完成,此时我们可以使用Spring 的事件来完成这样的需求:
事件:
可以继承ApplicationEvent表示一个事件
public class OrderSuccessEvent extends ApplicationEvent implements Serializable {
private static final long serialVersionUID = 0L;
/**
* Create a new {@code ApplicationEvent}.
*
* @param source the object on which the event initially occurred or with
* which the event is associated (never {@code null})
*/
public OrderSuccessEvent(Object source) {
super(source);
}
}
订单服务类(使用事件发布器发布事件)
该订单完成后需要使用事件发布器发布事件:
@Service
public class OrderService implements ApplicationContextAware {
/**
* 事件发布器,他是ApplicationEventPublisher接口的实现
*/
private ApplicationContext applicationContext;
public void order() {
System.out.println("下订单成功");
// 发布事件 通知短信
applicationContext.publishEvent(new OrderSuccessEvent("下单成功"));
System.out.println("事件发送后,继续做其他事情");
}
/**
* 容器启动会回调该方法,注入ApplicationContext
* @param applicationContext the ApplicationContext object to be used by this object
* @throws BeansException
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
事件监听器,使用@EventListener注解来表示监听某个事件
事件监听我们也可以实现接口ApplicationListener来监听事件
@Service
public class SmsService {
@EventListener(OrderSuccessEvent.class)
public void sendMsg(OrderSuccessEvent event) {
String msg = (String) event.getSource();
System.out.println("收到事件,发送通知短信:" + msg );
}
}
测试类:
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
//测试事件
OrderService orderService = applicationContext.getBean(OrderService.class);
orderService.order();
}
运行结果:
下订单成功
收到事件,发送通知短信:下单成功
事件发送后,继续做其他事情
从结果我们可以看出下单成功后,发送了一个订单成功事件,由于短信服务使用注解@EventListener监听了该事件,那么就会调用发送短信通知方法,发送短信。从该demo中我们可以体会到spring的事件机制了。
spring 事件机制需要先定义事件,然后定义事件监听器,监听器可以在方法上标注解或者实现接口来实现,最后事件发布器可以使用容器注入等方式来实现。仔细体会上面的例子就可以理解spring 事件机制了,下面我们深入探究下Spring 是怎么样实现事件机制的。
原理猜想:
在探究源码前,我们可以猜想下,我们在业务类的方法上标注了@EventListener
注解,为什么当事件发布后,该方法就能感知到并执行? 如果你是熟悉Spring 生命周期机制就能猜想到,肯定是在创建对象的时候,有个后置处理器或者别的什么组件来干预进来,该组件的功能是来分析该bean有没有标注@EventListener
注解,有的话肯定会把标注了**@EventListener**的方法封装成什么东西注入容器。
然后当发布某个事件的时候,肯定是取到所有该事件的监听器,然后遍历循环执行。(设计模式观察者模式)
带着这样的猜想我们来看看源码:
发现EventListenerMethodProcessor:
在容器启动的时候,我们可以打上断点查看下BeanFactory下的beanDefinitonMap的定义信息,查看下有没有关于spring事件的bena定义信息(要熟悉理解beanDefinitonMap的作用)。
根据上面的截图我们可以发现2个和spring 监听器相关的定义信息,进去看类名我们可以发现是这2个类EventListenerMethodProcessor
和DefaultEventListenerFactory
,进行分析发现DefaultEventListenerFactory只是一个普通的bean,根据类名可以猜测出他的作用应该是用来创建监听器的。
下面我们看看EventListenerMethodProcessor继承类图:
如果熟悉spring容器的我们知道,实现了ApplicationContextAware根据回调可以获取到整个IOC容器,实现BeanFactoryPostProcessor的作用回回调postProcessBeanFactory增强IOC容器,而SmartInitializingSingleton这个作用是在bean创建完对象后会有afterSingletonsInstantiated这个回调。
下面我们只需要分析EventListenerMethodProcessor
的postProcessBeanFactory
和afterSingletonsInstantiated
2个方法即可,
postProcessBeanFactory方法的代码很简单就是获取到所有的事件工厂保存到IOC容器中。
afterSingletonsInstantiated的调用链:
如果一个类实现了SmartInitializingSingleton接口,那么在该类创建完对象后就会回调afterSingletonsInstantiated这个方法。
该方法会从容器中取出所有的beanName,然后遍历调用processBean取出来每一个bean
该方法我标注了详细的注解,我们可以看出会去判断该bean是否有标注@EventListener注解,如果有标注就会使用监听器工厂把标注的@EventListener方法封装成事件监听器ApplicationListenerMethodAdapter
,然后保存,具体过程看源码注释。默认情况下监听器工厂会使用DefaultEventListenerFactory
至此我们可以得出EventListenerMethodProcessor的作用和我们猜测的一样,就是把标注了@EventListener
的方法封装成监听器ApplicationListenerMethodAdapter
监听器ApplicationListenerMethodAdapter封装信息如下:
上面分析我们得出,spring会把标注了@EventListener的方法封装成事件监听器,
那么当事件发布后的方法执行过程是怎么样的呢? 其实有了上面的分析,我们可以很简单的知道,所有的事件监听器都是ApplicationListener
的实现,那么当发布时的时候,肯定会获取到所有的监听器,调用每一个监听器的onApplicationEvent方法来实现。这就是典型的观察者模式实现。事件监听器ApplicationListener接口源码如下:
下面我们来看看事件发布的调用源码:
事件的发布需要事件发布器ApplicationEventPublisher的publishEvent方法,而ApplicationContext继承了ApplicationEventPublisher,所有也可以用ApplicationContext。
下面publishEvent
代码调用链很简单,就不做过多解释,请看关键注释
通过上面的源码分析,不知道你明白了spring 的事件机制了没有,看10遍不如自己动手跟踪分析一遍。其实源码很简单,体会体会就知道了。