观察者事件机制应用及原理

[toc]

一、简介

在JAVA体系中,有支持实现事件监听机制,在Spring 中也专门提供了一套事件机制的接口,方便我们实现。比如我们可以实现当用户注册后,给他发送一封邮件告诉他注册成功的一些信息,比如用户订阅的主题更新了,通知用户注意及时查看等。

二、观察者模式

观察者模式还有很多其他的称谓,如发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

观察者模式一般包含以下几个对象:

Subject:

被观察的对象。它提供一系列方法来增加和删除观察者对象,同时它定义了通知方法notify()。目标类可以是接口,也可以是抽象类或具体类。

ConcreteSubject:

具体的观察对象。Subject的具体实现类,在这里实现通知事件。

Observer:

观察者。这里是抽象的观察者,观察者有一个或者多个。

ConcreteObserver:

具体的观察者。在这里维护观察对象的具体操作。

06-Spring事件监听机制及原理 - 图1

三、示例

首先需要一个(观察者),该接口有一个 update 方法,用于接收公众号推送通知

  1. public interface Observer {
  2. public void update(String message);
  3. }

微信用户,具体观察者(ConcrereObserver)。微信用户是观察者,里面实现了更新的方法:

  1. public class WeixinUser implements Observer {
  2. // 微信用户名
  3. private String name;
  4. public WeixinUser(String name) {
  5. this.name = name;
  6. }
  7. @Override
  8. public void update(String message) {
  9. System.out.println(name + "-" + message);
  10. }
  11. }

抽象主题(被观察者Subject),提供了attach、detach、notify三个方法:

  1. public interface Subject {
  2. /**
  3. * 增加订阅者
  4. * @param observer
  5. */
  6. public void attach(Observer observer);
  7. /**
  8. * 删除订阅者
  9. * @param observer
  10. */
  11. public void detach(Observer observer);
  12. /**
  13. * 通知订阅者更新消息
  14. */
  15. public void notify(String message);
  16. }

微信公众号是具体主题(具体被观察者),里面存储了订阅该公众号的微信用户,并实现了抽象主题中的方法:

  1. public class SubscriptionSubject implements Subject {
  2. //储存订阅公众号的微信用户
  3. private List<Observer> weixinUserlist = new ArrayList<Observer>();
  4. @Override
  5. public void attach(Observer observer) {
  6. weixinUserlist.add(observer);
  7. }
  8. @Override
  9. public void detach(Observer observer) {
  10. weixinUserlist.remove(observer);
  11. }
  12. @Override
  13. public void notify(String message) {
  14. for (Observer observer : weixinUserlist) {
  15. observer.update(message);
  16. }
  17. }
  18. }

测试代码

  1. public class MainTest {
  2. public static void main(String[] args) {
  3. SubscriptionSubject subject = new SubscriptionSubject();
  4. WeixinUser zhangsanObserver = new WeixinUser("zhangsan");
  5. WeixinUser lisiObserver = new WeixinUser("lisi");
  6. subject.attach(zhangsanObserver);
  7. subject.attach(lisiObserver);
  8. subject.notify("文章1");
  9. }
  10. }

四、Java 中的事件机制

我们以网购12306火车票为场景,假设以下一些操作。

用户购买火车票成功后,会触发发送短信操作告知用户买了哪个车的票。
用户购买火车票成功后,会触发发送邮件操作告知用户买了哪个车的票。

针对上面的场景,我们分析可以有如下几个对象或操作。
1.火车票对象,2.购买成功发短信操作,3.购买成功发邮件操作。

事件概念理解:
事件源:触发事件的对象,例如购买火车票成功后发短信的火车票就是就是事件源。
事件:对事件源进行操作产生的事件,例如购买成功后会产生发短信事件和发邮件事件。
事件监听器:对事件源产生的事件进行处理,可以对不同的事件设置不同的事件监听器,处理不同事件。
事件派发器:事件派发器主要处理事件的派发和事件监听器的管理,注册和删除事件监听器等。

Java中提供了基本的事件处理基类:

  1. EventObject:提供事件的基类,任何自定义事件都集成自该类;
  2. EventListener:对应事件监听器,提供事件监听器者接口,任何自定义的事件监听器都实现了该接口。

TrainTicket事件源类

  1. /**
  2. * 事件源类
  3. * @author wiliam
  4. *
  5. */
  6. public class TrainTicket {
  7. private String userName;
  8. private String ticketName;
  9. }

TrainTicketEvent购票事件基类

  1. public class TrainTicketEvent extends EventObject {
  2. public TrainTicketEvent(TrainTicket source) {
  3. super(source);
  4. }
  5. }

SendEmailEvent发送邮件事件

  1. public class SendEmailEvent extends TrainTicketEvent {
  2. private static final long serialVersionUID = 1L;
  3. private TrainTicket trainTicket;
  4. public SendEmailEvent(TrainTicket source) {
  5. super(source);
  6. this.trainTicket = source;
  7. }
  8. public TrainTicket getTrainTicket(){
  9. return trainTicket;
  10. }
  11. public String getEmailData(){
  12. if(trainTicket != null){
  13. String data = "发送邮件:"+trainTicket.getUserName() + "您好,您已成功购买火车票"+trainTicket.getTicketName();
  14. return data;
  15. }
  16. return "";
  17. }
  18. }

SendSMSEvent发送短信事件

  1. public class SendSMSEvent extends TrainTicketEvent{
  2. private static final long serialVersionUID = 1L;
  3. private TrainTicket trainTicket;
  4. public SendSMSEvent(TrainTicket source) {
  5. super(source);
  6. this.trainTicket = source;
  7. }
  8. public TrainTicket getTrainTicket() {
  9. return trainTicket;
  10. }
  11. public String getSMSData() {
  12. if (trainTicket != null) {
  13. String data = "发送短信:" + trainTicket.getUserName() + "您好,您已成功购买火车票" + trainTicket.getTicketName();
  14. return data;
  15. }
  16. return "";
  17. }
  18. }

TrainTicketListener火车票事件监听器接口

  1. /**
  2. * 火车票事件监听器接口
  3. * @author wiliam
  4. *
  5. */
  6. public interface TrainTicketListener extends EventListener {
  7. void handEvent(TrainTicketEvent tte);
  8. }

EmailAndSMSListener同时处理发送邮件事件和短信事件

  1. /**
  2. * 同时处理发送邮件事件和短信事件
  3. * @author wiliam
  4. *
  5. */
  6. public class EmailAndSMSListener implements TrainTicketListener {
  7. @Override
  8. public void handEvent(TrainTicketEvent tte) {
  9. if(tte instanceof SendEmailEvent){
  10. SendEmailEvent see = (SendEmailEvent)tte;
  11. System.out.println(see.getEmailData());
  12. } else if(tte instanceof SendSMSEvent){
  13. SendSMSEvent sse = (SendSMSEvent)tte;
  14. System.out.println(sse.getSMSData());
  15. } else{
  16. System.out.println("发送未知事件,无法处理");
  17. }
  18. }
  19. }

事件派发,派发给监听器进行处理

  1. public class TrainTicketPublisher {
  2. private List<TrainTicketListener> ttlList = new ArrayList<TrainTicketListener>();
  3. private static TrainTicketPublisher ttp;
  4. private TrainTicketPublisher() {
  5. Properties props = new Properties();
  6. try {
  7. props.load(TrainTicketPublisher.class.getClassLoader().getResourceAsStream("me/wiliam/config.properties"));
  8. } catch (IOException e) {
  9. e.printStackTrace();
  10. throw new RuntimeException("初始化监听器失败" + e.getMessage());
  11. }
  12. String listenerStr = props.getProperty("listener");
  13. String[] listenerClassNames = listenerStr.split(",");
  14. if (listenerClassNames != null && listenerClassNames.length > 0) {
  15. for (String listenerClassName : listenerClassNames) {
  16. try {
  17. Class listenerClass = Class.forName(listenerClassName);
  18. TrainTicketListener ttl = (TrainTicketListener) listenerClass.newInstance();
  19. ttlList.add(ttl);
  20. } catch (ClassNotFoundException e) {
  21. // TODO Auto-generated catch block
  22. e.printStackTrace();
  23. } catch (InstantiationException e) {
  24. // TODO Auto-generated catch block
  25. e.printStackTrace();
  26. } catch (IllegalAccessException e) {
  27. // TODO Auto-generated catch block
  28. e.printStackTrace();
  29. } catch (Exception e) {
  30. e.printStackTrace();
  31. }
  32. }
  33. }
  34. }
  35. public static TrainTicketPublisher instance() {
  36. if (ttp == null) {
  37. ttp = new TrainTicketPublisher();
  38. }
  39. return ttp;
  40. }
  41. public void publishEvent(TrainTicketEvent tte) {
  42. for (TrainTicketListener ttl : ttlList) {
  43. ttl.handEvent(tte);
  44. }
  45. }
  46. }

TestBuyTrainTicketSuccessEvent事件测试类

  1. /**
  2. * 事件测试类
  3. * @author wiliam
  4. *
  5. */
  6. public class TestBuyTrainTicketSuccessEvent {
  7. public static void main(String[] args) {
  8. TestBuyTrainTicketSuccessEvent tbtts = new TestBuyTrainTicketSuccessEvent();
  9. tbtts.buySuccessTicket();
  10. }
  11. private void buySuccessTicket() {
  12. TrainTicket tt = new TrainTicket();
  13. tt.setTicketName("【北京---济南】");
  14. tt.setUserName("小明");
  15. System.out.println("购票成功");
  16. TrainTicketPublisher.instance().publishEvent(new SendEmailEvent(tt));
  17. TrainTicketPublisher.instance().publishEvent(new SendSMSEvent(tt));
  18. }
  19. }

五、Spring 中的事件机制

在 Spring 容器中通过ApplicationEven类和 ApplicationListener接口来实现事件监听机制,每次Event 被发布到Spring容器中时都会通知该Listener。需要注意的是,Spring 的事件默认是同步的,调用 publishEvent 方法发布事件后,它会处于阻塞状态,直到Listener接收到事件并处理返回之后才继续执行下去。

一、定义事件对象

  1. @Getter
  2. @Setter
  3. @ToString
  4. public class UserDTO extends ApplicationEvent{
  5. private Integer userId;
  6. private String name;
  7. private Integer age;
  8. public UserDTO(Object source){
  9. super(source);
  10. }
  11. }

二、定义事件监听器,可以通过注解或者实现接口来实现。

  1. @Component
  2. public class UserRegisterSmsListener{
  3. // 通过注解实现监听器
  4. @EventListener
  5. public void handleUserEvent(UserDTO userDTO){
  6. System.out.println("监听到用户注册,准备发送短信,user:"+userDTO.toString());
  7. }
  8. }
  9. // 通过实现接口实现监听器
  10. @Component
  11. public class UserRegisterEmailListener implements ApplicationListener<UserDTO>{
  12. @Override
  13. public void onApplicationEvent(UserDTO userDTO){
  14. System.out.println("监听到用户注册,准备发送邮件,user:" + userDTO.toString());
  15. }
  16. }
  17. @Component
  18. public class UserRegisterMessageListener implements ApplicationListener<UserDTO>{
  19. @Override
  20. public void onApplicationEvent(UserDTO userDTO){
  21. System.out.println("监听到用户注册,给新用户发送首条站内短消息,user:" + userDTO.toString());
  22. }
  23. }

三、注册服务

  1. public interface UserService{
  2. void register();
  3. }
  4. @Service
  5. public class UserServiceImpl implements UserService{
  6. @Autowired
  7. private ApplicationEventPublisher eventPublisher;
  8. @Override
  9. public void register(){
  10. UserDTO userDTO = new UserDTO(this);
  11. userDTO.setAge(18);
  12. userDTO.setName("精灵王jinglingwang.cn");
  13. userDTO.setUserId(1001);
  14. System.out.println("register user");
  15. eventPublisher.publishEvent(userDTO);
  16. }
  17. }

四、测试

  1. @Autowired
  2. private UserService userService;
  3. @Test
  4. public void testUserEvent(){
  5. userService.register();
  6. }

六、指定监听器的顺序

监听器的发布顺序是按照 bean 自然装载的顺序执行的,Spring 支持两种方式来实现有序

6.1、一、实现SmartApplicationListener接口指定顺序。

把上面三个Listener都改成实现SmartApplicationListener接口,并指定getOrder的返回值,返回值越小,优先级越高。

  1. @Component
  2. public class UserRegisterMessageListener implements SmartApplicationListener{
  3. @Override
  4. public boolean supportsEventType(Class<? extends ApplicationEvent> eventType){
  5. return eventType == UserDTO.class;
  6. }
  7. @Override
  8. public boolean supportsSourceType(Class<?> sourceType){
  9. return true;
  10. }
  11. @Override
  12. public void onApplicationEvent(ApplicationEvent event){
  13. System.out.println("监听到用户注册,给新用户发送首条站内短消息,user:" + event.toString());
  14. }
  15. @Override
  16. public int getOrder(){
  17. return -1;
  18. }
  19. }

另外两个监听器的改造省略,指定改造后的UserRegisterSmsListener返回order为0,UserRegisterEmailListener的getOrder返回1,测试输出结果如下:

  1. register user
  2. 监听到用户注册,给新用户发送首条站内短消息,user:UserDTO(userId=1001, name=精灵王jinglingwang.cn, age=18)
  3. 监听到用户注册,准备发送短信,userUserDTO(userId=1001, name=精灵王jinglingwang.cn, age=18)
  4. 监听到用户注册,准备发送邮件,user:UserDTO(userId=1001, name=精灵王jinglingwang.cn, age=18)

6.2、二、使用注解@Order()

  1. @Component
  2. public class UserRegisterSmsListener{
  3. @Order(-2)
  4. @EventListener
  5. public void handleUserEvent(UserDTO userDTO){
  6. System.out.println("监听到用户注册,准备发送短信,user:"+userDTO.toString());
  7. }
  8. }

测试输出结果如下:

  1. register user
  2. 监听到用户注册,准备发送短信,user:UserDTO(userId=1001, name=精灵王jinglingwang.cn, age=18)
  3. 监听到用户注册,给新用户发送首条站内短消息,user:UserDTO(userId=1001, name=精灵王jinglingwang.cn, age=18)
  4. 监听到用户注册,准备发送邮件,user:UserDTO(userId=1001, name=精灵王jinglingwang.cn, age=18)

七、异步支持

Spring 事件机制默认是同步阻塞的,如果 ApplicationEventPublisher 发布事件之后他会一直阻塞等待listener 响应,多个 listener 的情况下前面的没有执行完后面的会一直被阻塞。这时候我们可以利用 Spring 提供的线程池注解 @Async 来实现异步线程

一、使用 @Async 之前需要先开启线程池,在 启动类上添加 @EnableAsync 注解即可。

  1. @EnableAsync
  2. @SpringBootApplication
  3. public class DemoApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(DemoApplication.class, args);
  6. }
  7. }

二、监听器使用异步线程

自定义异步线程池

  1. @Configuration
  2. public class AsyncConfig{
  3. @Bean("asyncThreadPool")
  4. public Executor getAsyncExecutor(){
  5. System.out.println("asyncThreadPool init");
  6. Executor executor = new ThreadPoolExecutor(
  7. 10,20,60L,TimeUnit.SECONDS
  8. ,new ArrayBlockingQueue<>(100),new MyThreadFactory());
  9. return executor;
  10. }
  11. class MyThreadFactory implements ThreadFactory{
  12. final AtomicInteger threadNumber = new AtomicInteger(0);
  13. @Override
  14. public Thread newThread(Runnable r){
  15. Thread t = new Thread(r);
  16. t.setName("async-thread-"+threadNumber.getAndIncrement());
  17. t.setDaemon(true);
  18. return t;
  19. }
  20. }
  21. }

指定监听器的线程池

  1. @Component
  2. public class UserRegisterSmsListener{
  3. @Order(-2)
  4. @Async("asyncThreadPool")
  5. @EventListener
  6. public void handleUserEvent(UserDTO userDTO){
  7. System.out.println(Thread.currentThread().getName() + " 监听到用户注册,准备发送短信,user:"+userDTO.toString());
  8. }
  9. }

测试结果:

  1. register user
  2. 监听到用户注册,给新用户发送首条站内短消息,user:UserDTO(userId=1001, name=admol, age=18)
  3. 监听到用户注册,准备发送邮件,user:UserDTO(userId=1001, name=admol, age=18)
  4. async-thread-0 监听到用户注册,准备发送短信,user:UserDTO(userId=1001, name=admol, age=18)

八、Spring事件机制原理分析

Spring事件机制涉及的重要类主要有以下四个:

ApplicationEvent
事件对象,继承至JDK的类EventObject ,可以携带事件的时间戳

ApplicationListener
事件监听器,继承至JDK的接口EventListener,该接口被所有的事件监听器实现,比如支持指定顺序的SmartApplicationListener

ApplicationEventMulticaster
事件管理者,管理监听器和发布事件,ApplicationContext通过委托ApplicationEventMulticaster来 发布事件

ApplicationEventPublisher
事件发布者,该接口封装了事件有关的公共方法,作为ApplicationContext的超级街廓,也是委托 ApplicationEventMulticaster完成事件发布。

源码展示

**ApplicationEvent**

事件对象ApplicationEvent的主要源代码如下,继承了JAVA的EventObject 对象:

  1. public abstract class ApplicationEvent extends EventObject {
  2. private static final long serialVersionUID = 7099057708183571937L;
  3. private final long timestamp; // 多了一个时间戳属性
  4. public ApplicationEvent(Object source) {
  5. super(source);
  6. this.timestamp = System.currentTimeMillis(); // 初始当前化时间戳
  7. }
  8. public final long getTimestamp() {
  9. return this.timestamp;
  10. }
  11. }

06-Spring事件监听机制及原理 - 图2

从上面ApplicationEvent的子类关系图种可以发现,ApplicationEvent有一个重要的子类ApplicationContextEvent,而ApplicationContextEvent又有4个重要的子类ContextStartedEventContextRefreshedEventContextClosedEventContextStoppedEvent

从名字就可以看出,这4个事件都和Spring容器有关系的:

  • ContextRefreshedEvent:当spring容器context刷新时触发
  • ContextStartedEvent:当spring容器context启动后触发
  • ContextStoppedEvent:当spring容器context停止时触发
  • ContextClosedEvent:当spring容器context关闭时触发,容器被关闭时,其管理的所有单例Bean都被销毁。

当每个事件触发时,相关的监听器就会监听到相应事件,然后触发onApplicationEvent方法。

8.1、ApplicationListener

事件监听器,继承JDK的接口EventListener

  1. /* ...
  2. * @author Rod Johnson
  3. * @author Juergen Hoeller
  4. * @param <E> the specific ApplicationEvent subclass to listen to
  5. * @see org.springframework.context.event.ApplicationEventMulticaster
  6. */
  7. public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
  8. /**
  9. * Handle an application event. by jinglingwang.cn
  10. * @param event the event to respond to
  11. */
  12. void onApplicationEvent(E event);
  13. }

注释@param <E> the specific ApplicationEvent subclass to listen to@see ApplicationEventMulticaster 里面说明了事件的广播在ApplicationEventMulticaster类。

8.2 ApplicationEventMulticaster

ApplicationEventMulticaster是一个接口,负责管理监听器和发布事件,定义了如下方法:

  1. addApplicationListener(ApplicationListener<?> listener) :新增一个listener;
  2. addApplicationListenerBean(String listenerBeanName):新增一个listener,参数为bean name;
  3. removeApplicationListener(ApplicationListener<?> listener):删除listener;
  4. void removeAllListeners():删除所有的Listener
  5. removeApplicationListenerBean(String listenerBeanName):根据bean name 删除listener;
  6. multicastEvent(ApplicationEvent event):广播事件;
  7. multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType):广播事件,指定事件的source类型。

AbstractApplicationEventMulticaster实现了 ApplicationEventMulticaster接口,SimpleApplicationEventMulticaster 继承了AbstractApplicationEventMulticaster ;

  1. AbstractApplicationEventMulticaster 主要实现了管理监听器的方法(上面接口的前5个方法)
  2. SimpleApplicationEventMulticaster 主要实现了事件广播相关的方法(上面接口的最后2个方法)

两个类分别继承了部分上面的方法。

一、先看新增Listener方法实现逻辑:

  1. public abstract class AbstractApplicationEventMulticaster
  2. implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {
  3. private final ListenerRetriever defaultRetriever = new ListenerRetriever(false);
  4. ...
  5. @Override
  6. public void addApplicationListener(ApplicationListener<?> listener) {
  7. synchronized (this.retrievalMutex) { // 加排他锁
  8. // Explicitly remove target for a proxy, if registered already,
  9. // in order to avoid double invocations of the same listener.
  10. Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
  11. if (singletonTarget instanceof ApplicationListener) {
  12. // 删除,避免重复调用
  13. this.defaultRetriever.applicationListeners.remove(singletonTarget);
  14. }
  15. // 加入到Set LinkedHashSet 集合中
  16. this.defaultRetriever.applicationListeners.add(listener);
  17. this.retrieverCache.clear(); // 缓存
  18. }
  19. }
  20. ...
  21. }

最核心的一句代码:this.defaultRetriever.applicationListeners.add(listener);

ListenerRetriever类是AbstractApplicationEventMulticaster类的内部类,里面有两个集合,用来记录维护事件监听器

  1. private class ListenerRetriever {
  2. public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
  3. public final Set<String> applicationListenerBeans = new LinkedHashSet<>();
  4. ...
  5. }

这就和设计模式中的发布订阅模式一样了,维护一个List,用来管理所有的订阅者,当发布者发布消息时,遍历对应的订阅者列表,执行各自的回调handler。

二、看SimpleApplicationEventMulticaster类实现的广播事件逻辑:

  1. @Override
  2. public void multicastEvent(ApplicationEvent event) {
  3. multicastEvent(event, resolveDefaultEventType(event)); // 继续调用下面的广播方法
  4. }
  5. @Override
  6. public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
  7. ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
  8. // 遍历监听器列表
  9. for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
  10. Executor executor = getTaskExecutor();
  11. if (executor != null) { // 是否指定了线程池
  12. executor.execute(new Runnable() {
  13. @Override
  14. public void run() { // 线程池执行
  15. invokeListener(listener, event);
  16. }
  17. });
  18. }
  19. else { // 普通执行
  20. invokeListener(listener, event);
  21. }
  22. }
  23. }

代码分析:

  1. 首先根据事件类型,获取事件监听器列表:getApplicationListeners(event, type)
  2. 遍历监听器列表,for循环
  3. 判断是否有线程池,如果有,在线程池执行
  4. 否则直接执行

我们再看看 invokeListener方法的逻辑:

  1. protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
  2. ErrorHandler errorHandler = getErrorHandler();
  3. if (errorHandler != null) { // 是否有错误处理
  4. try {
  5. doInvokeListener(listener, event);
  6. } catch (Throwable err) {
  7. errorHandler.handleError(err);
  8. }
  9. } else {
  10. doInvokeListener(listener, event); // 直接执行
  11. }
  12. }

核心逻辑就是继续调用doInvokeListener方法:

  1. private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
  2. try {
  3. listener.onApplicationEvent(event);// 执行监听器事件
  4. }
  5. catch (ClassCastException ex) {
  6. String msg = ex.getMessage();
  7. if (msg == null || msg.startsWith(event.getClass().getName())) {
  8. // Possibly a lambda-defined listener which we could not resolve the generic event type for
  9. Log logger = LogFactory.getLog(getClass());
  10. if (logger.isDebugEnabled()) {
  11. logger.debug("Non-matching event type for listener: " + listener, ex);
  12. }
  13. }
  14. else {
  15. throw ex;
  16. }
  17. }
  18. }

发现最后实际就是调用的 listener.onApplicationEvent(event); 也就是我们通过实现接口ApplicationListener的方式来实现监听器的onApplicationEvent实现逻辑。

8.3、ApplicationEventPublisher

在我们的发布事件逻辑代码的地方,通过查看 eventPublisher.publishEvent(userDTO);方法可以发现ApplicationEventPublisher是一个接口,publishEvent方法的逻辑实现主要在类AbstractApplicationContext中:

  1. public abstract class AbstractApplicationContext extends DefaultResourceLoader
  2. implements ConfigurableApplicationContext, DisposableBean {
  3. ...
  4. private Set<ApplicationEvent> earlyApplicationEvents;
  5. ...
  6. @Override
  7. public void publishEvent(ApplicationEvent event) {
  8. publishEvent(event, null); // 调用下面的方法
  9. }
  10. // 发布事件主要逻辑
  11. protected void publishEvent(Object event, ResolvableType eventType) {
  12. Assert.notNull(event, "Event must not be null");
  13. if (logger.isTraceEnabled()) {
  14. logger.trace("Publishing event in " + getDisplayName() + ": " + event);
  15. }
  16. // 事件装饰为 ApplicationEvent
  17. ApplicationEvent applicationEvent;
  18. if (event instanceof ApplicationEvent) {
  19. applicationEvent = (ApplicationEvent) event;
  20. } else {
  21. applicationEvent = new PayloadApplicationEvent<Object>(this, event);
  22. if (eventType == null) {
  23. eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();
  24. }
  25. }
  26. // 如果这个集合已经初始化了,就把事件加入到集合中
  27. if (this.earlyApplicationEvents != null) {
  28. this.earlyApplicationEvents.add(applicationEvent); // 加入到集合,同一广播
  29. } else {
  30. // 还没初始化,直接广播事件
  31. getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
  32. }
  33. // 通过父上下文发布事件.
  34. if (this.parent != null) {
  35. if (this.parent instanceof AbstractApplicationContext) {
  36. ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
  37. }
  38. else {
  39. this.parent.publishEvent(event);
  40. }
  41. }
  42. }
  43. ...
  44. }

这段代码的主要逻辑在这:

  1. // 如果这个集合已经初始化了,就把事件加入到集合中
  2. if (this.earlyApplicationEvents != null) {
  3. this.earlyApplicationEvents.add(applicationEvent);
  4. }
  5. else {
  6. // 否则直接调用multicastEvent执行事件监听逻辑
  7. getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
  8. }

可以发现earlyApplicationEvents也是一个Set集合,如果这个集合已经初始化了,就把事件加入到集合中,否则直接调用multicastEvent执行事件监听逻辑。

我们跟踪找到初始化这个集合的地方,发现在方法protected void prepareRefresh()中:

  1. protected void prepareRefresh() {
  2. this.startupDate = System.currentTimeMillis();
  3. this.closed.set(false);
  4. this.active.set(true);
  5. if (logger.isInfoEnabled()) {
  6. logger.info("Refreshing " + this);
  7. }
  8. initPropertySources();
  9. getEnvironment().validateRequiredProperties();
  10. // 重要的方法
  11. this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
  12. }

继续跟踪调用这个方法的地方,发现在AbstractApplicationContext.refresh()方法中,而这个方法是Spring容器初始化必须要调用的过程,非常的重要。

那在什么地方使用到了这个集合呢?我们继续跟踪发现在 protected void registerListeners() 方法中,代码如下:

  1. protected void registerListeners() {
  2. // Register statically specified listeners first.
  3. for (ApplicationListener<?> listener : getApplicationListeners()) {
  4. getApplicationEventMulticaster().addApplicationListener(listener);
  5. }
  6. // Do not initialize FactoryBeans here: We need to leave all regular beans
  7. // uninitialized to let post-processors apply to them! jinglingwang.cn
  8. String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
  9. for (String listenerBeanName : listenerBeanNames) {
  10. getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
  11. }
  12. // 拿到集合引用
  13. Set<ApplicationEvent> ****earlyEventsToProcess = this.earlyApplicationEvents;
  14. this.earlyApplicationEvents = null; // 把之前的集合置为null
  15. if (earlyEventsToProcess != null) { // 如果集合不为空,则广播里面的事件
  16. for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
  17. getApplicationEventMulticaster().multicastEvent(earlyEvent);
  18. }
  19. }
  20. }

逻辑是先获得该集合的引用,然后置空之前的集合,然后遍历集合,进行广播事件multicastEvent,这个方法的逻辑上面已经说过了。

registerListeners这个方法是在什么时候调用的呢?通过跟踪发现也是在AbstractApplicationContext.refresh()方法中。

只不过基本是在方法逻辑的最后,也就是Spring已经容器初始化完成了。

  1. @Override
  2. public void refresh() throws BeansException, IllegalStateException {
  3. synchronized (this.startupShutdownMonitor) {
  4. // Prepare this context for refreshing.
  5. **prepareRefresh**();
  6. ....
  7. try {
  8. onRefresh();
  9. // Check for listener beans and register them.
  10. **registerListeners**();
  11. // Instantiate all remaining (non-lazy-init) singletons.
  12. finishBeanFactoryInitialization(beanFactory);
  13. // Last step: publish corresponding event.
  14. **finishRefresh**();
  15. }
  16. catch (BeansException ex) {
  17. ...
  18. }
  19. finally {
  20. ...
  21. }
  22. }
  23. }
  1. 事件监听机制和观察者模式非常相似
  2. JDK 也有实现提供事件监听机制
  3. Spring 的事件机制也是基于JDK 来扩展的
  4. Spring 的事件机制默认是同步阻塞的
  5. Spring 容器初始化前后都可能进行广播事件

参考

https://www.cnblogs.com/admol/p/14036564.html Spring 事件监听机制及原理分析

https://www.jianshu.com/p/ec00575b8ae8 java中事件机制原理解析