spring事件通知机制详解
优势
- 解耦
- 对同一种事件有多种处理方式
- 不干扰主线(main line)
起源
要讲spring的事件通知机制,就要先了解一下spring中的这些接口和抽象类:
ApplicationEventPublisherAware 接口:用来 publish event
ApplicationEvent 抽象类,记录了source和初始化时间戳:用来定义Event
ApplicationListener<E extends ApplicationEvent> :用来监听事件
构建自己的事件机制案例
测试案例
测试入口
package com.meituan.spring.testcase.listener; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.util.concurrent.TimeUnit; /**
* Created by zhangxiaoguang on 16/1/27 下午11:40.
* -----------------------------
* Desc:
*/
public class TestPortal {
public static void main(String[] args) throws InterruptedException { final ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml"); String[] definitionNames = applicationContext.getBeanDefinitionNames();
System.out.println("==============bean====start=================");
for (String definitionName : definitionNames) {
System.out.println("bean----:" + definitionName);
}
System.out.println("==============bean====end=================");
System.out.println();
final CustomizePublisher customizePublisher = applicationContext.getBean(CustomizePublisher.class); Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("开始吃饭:"); MealEvent lunchEvent = new MealEvent("A吃午饭了", MealEnum.lunch);
MealEvent breakfastEvent = new MealEvent("B吃早饭了", MealEnum.breakfast);
MealEvent dinnerEvent = new MealEvent("C吃晚饭了", MealEnum.dinner);
customizePublisher.publish(lunchEvent);
TimeUnit.SECONDS.sleep(1l);
customizePublisher.publish(breakfastEvent);
TimeUnit.SECONDS.sleep(1l);
customizePublisher.publish(dinnerEvent);
TimeUnit.SECONDS.sleep(1l); System.out.println("他们吃完了!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.setName("meal-thread");
thread.start(); System.out.println(Thread.currentThread().getName() + " is waiting for ....");
thread.join();
System.out.println("Done!!!!!!!!!!!!");
}
}
TestPortal
测试结果
测试成员
MealListener :MealEvent 演员
TroubleListener :TroubleEvent 演员
AllAcceptedListener 演员
MealEnum 道具
TestPortal 入口
CustomizePublisher 导演
成员代码
接受全部事件的演员(很负责任啊)
package com.meituan.spring.testcase.listener; import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component; /**
* Created by zhangxiaoguang on 16/1/27 下午11:27.
* -----------------------------
* Desc:
*/
@Component
public class AllAcceptedListener implements ApplicationListener<ApplicationEvent> {
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println(">>>>>>>>>>>>>>>>event:" + event);
}
}
AllAcceptedListener
导演负责分发事件
package com.meituan.spring.testcase.listener; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component; /**
* Created by zhangxiaoguang on 16/1/28 上午1:41.
* -----------------------------
* Desc:
*/
@Component
public class CustomizePublisher implements ApplicationEventPublisherAware { private ApplicationEventPublisher applicationEventPublisher; public void publish(MealEvent event) {
applicationEventPublisher.publishEvent(event);
} @Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
}
CustomizePublisher
负责处理吃饭事件的演员
package com.meituan.spring.testcase.listener; import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component; /**
* Created by zhangxiaoguang on 16/1/27 下午11:27.
* -----------------------------
* Desc:
*/
@Component
public class MealListener implements ApplicationListener<MealEvent> {
@Override
public void onApplicationEvent(MealEvent event) {
System.out.println(String.format(">>>>>>>>>>>thread:%s,type:%s,event:%s",
Thread.currentThread().getName(), event.getMealEnum(), event)); dispatchEvent(event);
} private void dispatchEvent(MealEvent event) {
switch (event.getMealEnum()) {
case breakfast:
System.out.println(event.getMealEnum() + " to handle!!!");
break;
case lunch:
System.out.println(event.getMealEnum() + " to handle!!!");
break;
case dinner:
System.out.println(event.getMealEnum() + " to handle!!!");
break;
default:
System.out.println(event.getMealEnum() + " error!!!");
break;
}
}
}
MealListener
吃饭消息
package com.meituan.spring.testcase.listener; import org.springframework.context.ApplicationEvent; /**
* Created by zhangxiaoguang on 16/1/27 下午11:24.
* -----------------------------
* Desc:吃饭事件
*/
public class MealEvent extends ApplicationEvent { private MealEnum mealEnum; /**
* @param mealContent
* 吃什么
* @param mealEnum
* 早餐还是午餐?
*/
public MealEvent(String mealContent, MealEnum mealEnum) {
super(mealContent);
this.mealEnum = mealEnum;
} public MealEnum getMealEnum() {
return mealEnum;
}
}
MealEvent
工具
package com.meituan.spring.testcase.listener; /**
* Created by zhangxiaoguang on 16/1/27 下午11:29.
* -----------------------------
* Desc:
*/
public enum MealEnum {
breakfast,
lunch,
dinner
}
MealEnum
令人厌烦的演员
package com.meituan.spring.testcase.listener; import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component; /**
* Created by zhangxiaoguang on 16/1/27 下午11:27.
* -----------------------------
* Desc:
*/
@Component
public class TroubleListener implements ApplicationListener<TroubleEvent> {
@Override
public void onApplicationEvent(TroubleEvent event) {
System.out.println(">>>>>>>>>>>>>>>>event:" + event);
}
}
TroubleListener
令人厌烦的事件
package com.meituan.spring.testcase.listener; import org.springframework.context.ApplicationEvent; /**
* Created by zhangxiaoguang on 16/1/27 下午11:24.
* -----------------------------
* Desc:令人厌烦的事件
*/
public class TroubleEvent extends ApplicationEvent {
public TroubleEvent(Object source) {
super(source);
}
}
TroubleEvent
总结
详细定制 event 类型的,则相关定制的listener会处理对应的消息,其他listener不会管闲事;
制定顶级 event 类型的,ApplicationEvent的,则会处理所有的事件。
ApplicationEvent
依赖关系

ContextEvent事件机制简介
ContextRefreshedEvent:当整个ApplicationContext容器初始化完毕或者刷新时触发该事件;
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
...... try { ...... // Last step: publish corresponding event.
finishRefresh();
} catch (BeansException ex) {
......
}
}
}
protected void finishRefresh() {
// Initialize lifecycle processor for this context.
initLifecycleProcessor(); // Propagate refresh to lifecycle processor first.
getLifecycleProcessor().onRefresh(); // Publish the final event.
publishEvent(new ContextRefreshedEvent(this)); // Participate in LiveBeansView MBean, if active.
LiveBeansView.registerApplicationContext(this);
}
ContextClosedEvent:当ApplicationContext doClose时触发该事件,这个时候会销毁所有的单例bean;
@Override
public void registerShutdownHook() {
if (this.shutdownHook == null) {
// No shutdown hook registered yet.
this.shutdownHook = new Thread() {
@Override
public void run() {
doClose();
}
};
Runtime.getRuntime().addShutdownHook(this.shutdownHook);
}
}
@Override
public void close() {
synchronized (this.startupShutdownMonitor) {
doClose();
// If we registered a JVM shutdown hook, we don't need it anymore now:
// We've already explicitly closed the context.
if (this.shutdownHook != null) {
try {
Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
}
catch (IllegalStateException ex) {
// ignore - VM is already shutting down
}
}
}
}
protected void doClose() {
if (this.active.get() && this.closed.compareAndSet(false, true)) {
......
try {
// Publish shutdown event.
publishEvent(new ContextClosedEvent(this));
}
catch (Throwable ex) {
logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
}
......
}
}
ContextStartedEvent:当ApplicationContext start时触发该事件;
@Override
public void start() {
getLifecycleProcessor().start();
publishEvent(new ContextStartedEvent(this));
}
ContextStoppedEvent:当ApplicationContext stop时触发该事件;
@Override
public void stop() {
getLifecycleProcessor().stop();
publishEvent(new ContextStoppedEvent(this));
}
ApplicationListener
依赖关系

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












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

如何设置线程池?
回到上边的问题,到底该如何设置线程池呢?
AbstractApplicationEventMulticaster 是private的,并且没有提供写入方法...
实际案例
用在自己的代码里就是最好的例子了 ^_^
spring事件通知机制详解的更多相关文章
- Android事件分发机制详解
事件分发机制详解 一.基础知识介绍 1.经常用的事件有:MotionEvent.ACTION_DOWN,MotionEvent.ACTION_MOVE,MotionEvent.ACTION_UP等 2 ...
- Android事件传递机制详解及最新源码分析——ViewGroup篇
版权声明:本文出自汪磊的博客,转载请务必注明出处. 在上一篇<Android事件传递机制详解及最新源码分析--View篇>中,详细讲解了View事件的传递机制,没掌握或者掌握不扎实的小伙伴 ...
- Android 的事件传递机制,详解
Android 的事件传递机制,详解 前两天和一个朋友聊天的时候.然后说到事件传递机制.然后让我说的时候,忽然发现说的不是非常清楚,事实上Android 的事件传递机制也是知道一些,可是感觉自己知道的 ...
- Android Touch事件传递机制详解 下
尊重原创:http://blog.csdn.net/yuanzeyao/article/details/38025165 资源下载:http://download.csdn.net/detail/yu ...
- Android开发——事件分发机制详解
0. 前言 转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/52566965 深入学习事件分发机制,是为了解决在Android开发中 ...
- IOS 触摸事件分发机制详解
欢迎大家前往云+社区,获取更多腾讯海量技术实践干货哦~ 作者:MelonTeam 前言 很多时候大家都不关心IOS触摸事件的分发机制的实现原理,当遇到以下几种情形的时候你很可能抓破头皮都找不到解决方案 ...
- 【Android面试查漏补缺】之事件分发机制详解
前言 查漏补缺,查漏补缺,你不知道哪里漏了,怎么补缺呢?本文属于[Android面试查漏补缺]系列文章第一篇,持续更新中,感兴趣的朋友可以[关注+收藏]哦~ 本系列文章是对自己的前段时间面试经历的总结 ...
- android 事件分发机制详解(OnTouchListener,OnClick)
昨天做东西做到触摸事件冲突,以前也经常碰到事件冲突,想到要研究一下Android的事件冲突机制,于是从昨天开始到今天整整一天时间都要了解这方面的知识,这才懂了安卓的触摸和点击事件的机制.探究如下: 首 ...
- Android View 事件分发机制详解
想必很多android开发者都遇到过手势冲突的情况,我们一般都是通过内部拦截和外部拦截法解决此类问题.要想搞明白原理就必须了解View的分发机制.在此之前我们先来了解一下以下三个非常重要的方法: di ...
随机推荐
- Android Studio 经常使用功能介绍
为了简化 Android 的开发力度,Google 决定将重点建设 Android Studio 工具.Google 会在今年年底停止支持其它集成开发环境.比方 Eclipse. Android St ...
- CvMat 矩阵的使用方法和简单程序
一:CvMat* cvInitMatHeader( CvMat* mat, int rows, int cols, int type,void* data=NULL, int step=CV_AUTO ...
- HTML5图片旋转
HTML5图片旋转 首先我们使用Cococs2dx-Js-Lite版,来创建一个工程,我们所需要的开发环境如下: 1,webstrom 2,google chrome浏览器 3,cocos2dx-Js ...
- [leetode]Binary Search Tree Iterator
用个stack模拟递归即可 /** * Definition for binary tree * struct TreeNode { * int val; * TreeNode *left; * Tr ...
- uniGUI试用笔记(十五)通过URL控制参数
通过URL代入参数,在代码中读取,如: http://localhost:8501/?ServerPort=212&&ServerIP=192.168.31.12 在代码中可以通过: ...
- 关于Linux session管理与GUI架构
google了一下感觉一下子找不到太好的资料,可能需要慢慢深入去学习. 这里有一个讲session management的,还算比较深入: https://dvdhrm.wordpress.com/2 ...
- [Z]CS权威会议
CS Conference TOP 40 计算机会议TOP40 一.A 类 15 个 ASPLOS: Architecture Support for Programming Languages an ...
- DevExpress.XtraGrid winform试用分享
DevExpress.XtraGrid在winform里使用还挺麻烦,为了减少重复代码和代码复用,本人针对DevExpress.XtraGrid封装了一个Form的基类,其中涵盖了DevExpress ...
- 一个程序说明标准i/O缓冲的效率
来瞧一个程序,了解一下标准I/O中的缓冲区对程序运行的影响: #include<stdio.h>#include<unistd.h> int main(){ char a[2] ...
- android手势创建及识别
使用一些浏览器或者输入法应用时会有一些手势操作,还可以自定义手势.这些神奇的操作是怎么做的呢?这一篇重点记录手势的识别和创建.这篇的内容使用到的是android.gesture包,具体的例子参考的是S ...
