优势

  • 解耦
  • 对同一种事件有多种处理方式
  • 不干扰主线(main line)

起源

要讲spring的事件通知机制,就要先了解一下spring中的这些接口和抽象类:

  • ApplicationEventPublisherAware        接口:用来 publish event

  • ApplicationEvent                  抽象类,记录了source和初始化时间戳:用来定义Event

  • ApplicationListener<E extends ApplicationEvent>  :用来监听事件

构建自己的事件机制案例

测试案例

测试入口

 1 package com.meituan.spring.testcase.listener;
 2
 3 import org.springframework.context.support.ClassPathXmlApplicationContext;
 4
 5 import java.util.concurrent.TimeUnit;
 6
 7 /**
 8  * Created by zhangxiaoguang on 16/1/27 下午11:40.
 9  * -----------------------------
10  * Desc:
11  */
12 public class TestPortal {
13    public static void main(String[] args) throws InterruptedException {
14
15       final ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");
16
17       String[] definitionNames = applicationContext.getBeanDefinitionNames();
18       System.out.println("==============bean====start=================");
19       for (String definitionName : definitionNames) {
20          System.out.println("bean----:" + definitionName);
21       }
22       System.out.println("==============bean====end=================");
23       System.out.println();
24       final CustomizePublisher customizePublisher = applicationContext.getBean(CustomizePublisher.class);
25
26
27       Thread thread = new Thread(new Runnable() {
28          @Override
29          public void run() {
30             try {
31                System.out.println("开始吃饭:");
32
33                MealEvent lunchEvent = new MealEvent("A吃午饭了", MealEnum.lunch);
34                MealEvent breakfastEvent = new MealEvent("B吃早饭了", MealEnum.breakfast);
35                MealEvent dinnerEvent = new MealEvent("C吃晚饭了", MealEnum.dinner);
36                customizePublisher.publish(lunchEvent);
37                TimeUnit.SECONDS.sleep(1l);
38                customizePublisher.publish(breakfastEvent);
39                TimeUnit.SECONDS.sleep(1l);
40                customizePublisher.publish(dinnerEvent);
41                TimeUnit.SECONDS.sleep(1l);
42
43                System.out.println("他们吃完了!");
44             } catch (InterruptedException e) {
45                e.printStackTrace();
46             }
47          }
48       });
49       thread.setName("meal-thread");
50       thread.start();
51
52       System.out.println(Thread.currentThread().getName() + " is waiting for ....");
53       thread.join();
54       System.out.println("Done!!!!!!!!!!!!");
55    }
56 }

TestPortal

测试结果

测试成员

  • MealListener :MealEvent                  演员

  • TroubleListener :TroubleEvent         演员

  • AllAcceptedListener                            演员

  • MealEnum                                          道具

  • TestPortal                                           入口

  • CustomizePublisher                           导演

成员代码

接受全部事件的演员(很负责任啊)

 1 package com.meituan.spring.testcase.listener;
 2
 3 import org.springframework.context.ApplicationEvent;
 4 import org.springframework.context.ApplicationListener;
 5 import org.springframework.stereotype.Component;
 6
 7 /**
 8  * Created by zhangxiaoguang on 16/1/27 下午11:27.
 9  * -----------------------------
10  * Desc:
11  */
12 @Component
13 public class AllAcceptedListener implements ApplicationListener<ApplicationEvent> {
14    @Override
15    public void onApplicationEvent(ApplicationEvent event) {
16       System.out.println(">>>>>>>>>>>>>>>>event:" + event);
17    }
18 }

AllAcceptedListener

导演负责分发事件

 1 package com.meituan.spring.testcase.listener;
 2
 3 import org.springframework.context.ApplicationEventPublisher;
 4 import org.springframework.context.ApplicationEventPublisherAware;
 5 import org.springframework.stereotype.Component;
 6
 7 /**
 8  * Created by zhangxiaoguang on 16/1/28 上午1:41.
 9  * -----------------------------
10  * Desc:
11  */
12 @Component
13 public class CustomizePublisher implements ApplicationEventPublisherAware {
14
15    private ApplicationEventPublisher applicationEventPublisher;
16
17    public void publish(MealEvent event) {
18       applicationEventPublisher.publishEvent(event);
19    }
20
21    @Override
22    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
23       this.applicationEventPublisher = applicationEventPublisher;
24    }
25 }

CustomizePublisher

负责处理吃饭事件的演员

 1 package com.meituan.spring.testcase.listener;
 2
 3 import org.springframework.context.ApplicationListener;
 4 import org.springframework.stereotype.Component;
 5
 6 /**
 7  * Created by zhangxiaoguang on 16/1/27 下午11:27.
 8  * -----------------------------
 9  * Desc:
10  */
11 @Component
12 public class MealListener implements ApplicationListener<MealEvent> {
13    @Override
14    public void onApplicationEvent(MealEvent event) {
15       System.out.println(String.format(">>>>>>>>>>>thread:%s,type:%s,event:%s",
16             Thread.currentThread().getName(), event.getMealEnum(), event));
17
18       dispatchEvent(event);
19    }
20
21    private void dispatchEvent(MealEvent event) {
22       switch (event.getMealEnum()) {
23          case breakfast:
24             System.out.println(event.getMealEnum() + " to handle!!!");
25             break;
26          case lunch:
27             System.out.println(event.getMealEnum() + " to handle!!!");
28             break;
29          case dinner:
30             System.out.println(event.getMealEnum() + " to handle!!!");
31             break;
32          default:
33             System.out.println(event.getMealEnum() + " error!!!");
34             break;
35       }
36    }
37 }

MealListener

吃饭消息

 1 package com.meituan.spring.testcase.listener;
 2
 3 import org.springframework.context.ApplicationEvent;
 4
 5 /**
 6  * Created by zhangxiaoguang on 16/1/27 下午11:24.
 7  * -----------------------------
 8  * Desc:吃饭事件
 9  */
10 public class MealEvent extends ApplicationEvent {
11
12    private MealEnum mealEnum;
13
14    /**
15     * @param mealContent
16     *        吃什么
17     * @param mealEnum
18     *        早餐还是午餐?
19     */
20    public MealEvent(String mealContent, MealEnum mealEnum) {
21       super(mealContent);
22       this.mealEnum = mealEnum;
23    }
24
25    public MealEnum getMealEnum() {
26       return mealEnum;
27    }
28 }

MealEvent

工具

 1 package com.meituan.spring.testcase.listener;
 2
 3 /**
 4  * Created by zhangxiaoguang on 16/1/27 下午11:29.
 5  * -----------------------------
 6  * Desc:
 7  */
 8 public enum MealEnum {
 9    breakfast,
10    lunch,
11    dinner
12 }

MealEnum

令人厌烦的演员

 1 package com.meituan.spring.testcase.listener;
 2
 3 import org.springframework.context.ApplicationListener;
 4 import org.springframework.stereotype.Component;
 5
 6 /**
 7  * Created by zhangxiaoguang on 16/1/27 下午11:27.
 8  * -----------------------------
 9  * Desc:
10  */
11 @Component
12 public class TroubleListener implements ApplicationListener<TroubleEvent> {
13    @Override
14    public void onApplicationEvent(TroubleEvent event) {
15       System.out.println(">>>>>>>>>>>>>>>>event:" + event);
16    }
17 }

TroubleListener

令人厌烦的事件

 1 package com.meituan.spring.testcase.listener;
 2
 3 import org.springframework.context.ApplicationEvent;
 4
 5 /**
 6  * Created by zhangxiaoguang on 16/1/27 下午11:24.
 7  * -----------------------------
 8  * Desc:令人厌烦的事件
 9  */
10 public class TroubleEvent extends ApplicationEvent {
11    public TroubleEvent(Object source) {
12       super(source);
13    }
14 }

TroubleEvent

总结

详细定制 event 类型的,则相关定制的listener会处理对应的消息,其他listener不会管闲事;

制定顶级 event 类型的,ApplicationEvent的,则会处理所有的事件。

ApplicationEvent

依赖关系

ContextEvent事件机制简介

ContextRefreshedEvent:当整个ApplicationContext容器初始化完毕或者刷新时触发该事件;

 1 @Override
 2 public void refresh() throws BeansException, IllegalStateException {
 3    synchronized (this.startupShutdownMonitor) {
 4       ......
 5
 6       try {
 7
 8          ......
 9
10          // Last step: publish corresponding event.
11          finishRefresh();
12       }
13
14       catch (BeansException ex) {
15          ......
16       }
17    }
18 }
19 protected void finishRefresh() {
20    // Initialize lifecycle processor for this context.
21    initLifecycleProcessor();
22
23    // Propagate refresh to lifecycle processor first.
24    getLifecycleProcessor().onRefresh();
25
26    // Publish the final event.
27    publishEvent(new ContextRefreshedEvent(this));
28
29    // Participate in LiveBeansView MBean, if active.
30    LiveBeansView.registerApplicationContext(this);
31 }

View Code

ContextClosedEvent:当ApplicationContext doClose时触发该事件,这个时候会销毁所有的单例bean;

 1 @Override
 2 public void registerShutdownHook() {
 3    if (this.shutdownHook == null) {
 4       // No shutdown hook registered yet.
 5       this.shutdownHook = new Thread() {
 6          @Override
 7          public void run() {
 8             doClose();
 9          }
10       };
11       Runtime.getRuntime().addShutdownHook(this.shutdownHook);
12    }
13 }
14 @Override
15 public void close() {
16    synchronized (this.startupShutdownMonitor) {
17       doClose();
18       // If we registered a JVM shutdown hook, we don't need it anymore now:
19       // We've already explicitly closed the context.
20       if (this.shutdownHook != null) {
21          try {
22             Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
23          }
24          catch (IllegalStateException ex) {
25             // ignore - VM is already shutting down
26          }
27       }
28    }
29 }
30 protected void doClose() {
31    if (this.active.get() && this.closed.compareAndSet(false, true)) {
32       ......
33
34       try {
35          // Publish shutdown event.
36          publishEvent(new ContextClosedEvent(this));
37       }
38       catch (Throwable ex) {
39          logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
40       }
41
42       ......
43    }
44 }

View Code

ContextStartedEvent:当ApplicationContext start时触发该事件;

1 @Override
2 public void start() {
3    getLifecycleProcessor().start();
4    publishEvent(new ContextStartedEvent(this));
5 }

ContextStoppedEvent:当ApplicationContext stop时触发该事件;

1 @Override
2 public void stop() {
3    getLifecycleProcessor().stop();
4    publishEvent(new ContextStoppedEvent(this));
5 }

ApplicationListener

依赖关系

带你一步步走向源码的世界

从上边打印的线程信息可以知道,spring处理事件通知采用的是当前线程,并没有为为我们启动新的线程,所以,如果需要,你要自己处理线程信息哦,当然也可以设定(如何设置?)!

AbstractApplicationContext

补齐:同一个event,被多个listener监听,先被哪个listener执行是由下边的代码决定的:

如何设置线程池?

回到上边的问题,到底该如何设置线程池呢?

AbstractApplicationEventMulticaster 是private的,并且没有提供写入方法...

实际案例

用在自己的代码里就是最好的例子了 ^_^

转载于:https://www.cnblogs.com/zhangxiaoguang/p/spring-notification.html

spring事件通知机制详解相关推荐

  1. epoll边缘触发_epoll事件通知机制详解,水平触发和边沿触发的区别

    看到网上有不少讨论epoll,但大多不够详细准确,以前面试有被问到这个问题.不去更深入的了解,只能停留在知其然而不知其所以然.于是,把epoll手册翻译一遍,更深入理解和掌握epoll事件处理相关知识 ...

  2. Android 系统(199)---Android事件分发机制详解

    Android事件分发机制详解 前言 Android事件分发机制是Android开发者必须了解的基础 网上有大量关于Android事件分发机制的文章,但存在一些问题:内容不全.思路不清晰.无源码分析. ...

  3. View的事件体系之三 android事件分发机制详解(下)

    接着上一篇来分析事件分发机制,在看了各位大牛的关于事件分发机制的分析后茅塞顿开,之前看过好几遍郭霖,弘扬以及玉刚大神关于事件体系的讲解,一直看不懂,比较模糊,最近复习时,看到一篇博文,写的相当精彩,看 ...

  4. 安卓自定义View进阶-事件分发机制详解

    原文地址:http://www.gcssloop.com/customview/dispatch-touchevent-source Android 事件分发机制详解,在上一篇文章 事件分发机制原理  ...

  5. node mysql 事件循环_NodeJs 的 Event loop 事件循环机制详解

    什么是事件轮询 事件循环是 Node.js 处理非阻塞 I/O 操作的机制--尽管 JavaScript 是单线程处理的--当有可能的时候,它们会把操作转移到系统内核中去. 下面的图表显示了事件循环的 ...

  6. Android事件分发机制详解

    2019独角兽企业重金招聘Python工程师标准>>> 之前在学习Android事件方法机制的时候,看过不少文章,但是大部分都讲的不是很清楚,我自己理解的也是云里雾里,也尝试过阅读源 ...

  7. Android View 事件分发机制详解

    想必很多android开发者都遇到过手势冲突的情况,我们一般都是通过内部拦截和外部拦截法解决此类问题.要想搞明白原理就必须了解View的分发机制.在此之前我们先来了解一下以下三个非常重要的方法: di ...

  8. python制作界面怎么触发事件_python模拟事件触发机制详解

    本文实例为大家分享了python模拟事件触发机制的具体代码,供大家参考,具体内容如下 EventManager.py # -*- encoding: UTF-8 -*- # 系统模块 from que ...

  9. python事件触发机制_python模拟事件触发机制详解

    本文实例为大家分享了python模拟事件触发机制的具体代码,供大家参考,具体内容如下 EventManager.py # -*- encoding: UTF-8 -*- # 系统模块 from que ...

最新文章

  1. 计算机科学与技术专业综合二,计算机科学与技术专业综合一第二页
  2. 关于在ajax中传递中文引起的乱码问题。
  3. linux中atoi函数的实现 值得借鉴,【转】atoi()函数的实现
  4. 在控制台输入一个整数,根据整数打印一个矩形 (Python)
  5. 低版本系统兼容的ActionBar(六)用Fragment+ViewPager+Tab实现快速导航
  6. 常见的shell命令
  7. GitHub使用教程详解(上)——官网操作指南[翻译]
  8. mix2线刷开发板救砖_小米MIX 2S手机开不了机_线刷救砖教程_安卓刷机工具_一键刷机...
  9. word文档打钩记录快捷键
  10. 90后HTTP 的爱情故事
  11. LeetCode Day01:一年中的第几天
  12. 《沟通的方法》笔记十七:道歉
  13. 大数据风控必看,挖掘学历数据中暗藏的还款意愿及还款能力
  14. (2013.05.05)N枚硬币找1枚假币
  15. huihoo和中国的开源,路在何方???
  16. 小米手机风筝守护显示服务器异常,风筝守护只能小米用吗
  17. 纽约大学研究生 计算机科学 申请,纽约大学计算机科学理学硕士理学硕士研究生申请要求及申请材料要求清单...
  18. 车贷乱象横生,修炼好内功方为易鑫车贷上上之策
  19. 前端时间转换 2019-08-18T16:00:00.000Z 转换成格式2019-08-18
  20. Runtime objc4-779.1 为什么不能向一个已存在的类添加成员变量?有什么办法达到相同的效果(2)?

热门文章

  1. HokeyPokey — WWDC讲师特供XCode插件高仿版的设计与实现
  2. 大话网站---从Hello World到高并发网站
  3. win bat 输入参数
  4. Sharepoint 2010 网站无法检索到部分AD用户
  5. 解决不同浏览器的css兼容问题
  6. 17秋 软件工程 团队第五次作业 Alpha
  7. lintcode 中等题:subSets 子集
  8. HDU 5239 上海大都会 D题(线段树+数论)
  9. ArcGIS Server 9.2 on Solaris/Linux 安装可能出现的问题
  10. adpcm 解码音量小_Oriolus 1795解码耳放评测:仅仅蓝牙还不够,我要的是“真无线”...