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 ...
随机推荐
- mongodb_查询操作使用_条件查询、where子句等(转)
<?php /* mongodb_查询操作使用_条件查询.where子句等(转并学习) 1.find()/findOne() mongodb数据库的查询操作即使用find()或者findO ...
- linux系统和依赖包常用下载地址
http://ftp.gnome.org/pub/gnome/sources/gstreamer/0.10/ http://www.linuxfromscratch.org/blfs/view/svn ...
- [AX2012 R3]在SSRS报表中使用QR二维码
AX2012是自带生成QR二维码的类,可以很方便的用在SSRS报表中,下面演示如何在RDP的报表中使用二维码,首先从定义临时表开始: 字段URL是要用于二维码的字符串,QrCode是container ...
- Schema Workbench 开发mdx和模式文件
一.前言 安装了saiku之后,每次修改schema文件,非常耗时,每次都要经历若干步骤:修改xml.上传.重启才能生效,并且非常不利于学习和理解MDX和模式文件,踌躇之际,发现了这个工具,十分小巧方 ...
- 批量Ping IP
刚刚接触Python 想做点什么 听说Python 在网络方便很厉害 后来总结如下: 第一:发现公司都固定IP 每次新来同事都要猜一个没有人用的IP 很费劲 第二:我们公司有的IP可以上QQ 有的不 ...
- 那些年我们赚过的外快(POS(移动支付)接口开发)
老规矩上前戏了.在我写博文"那些年我们赚过的外快"前后算起来大大小小也接了些私活,这次是因为好久没写博客了,趁热分享一下.最近回了离老家近的二线城市成都工作,收入那是下降很多啊,刚 ...
- Google首席软件工程师Joshua Bloch谈如何设计一款优秀的API【附PPT】
编者按]随着近来软件规模的日益庞大,API编程接口的设计变的越来越重要.良好的接口设计可以降低系统各部分之间的相互依赖,提高组成单元的内聚性,降低组成单元间的耦合度,从而提高系统的维护性和稳定性. J ...
- 清除SQL数据库的日志
ALTER DATABASE [Fuliu_Test] SET RECOVERY SIMPLEALTER DATABASE [Fuliu_Test] SET RECOVERY FULLDBCC SHR ...
- Android UI开发第四十篇——ScrollTricks介绍
ScrollTricks是一个开源控件,实现了两个简单功能: 1.Quick Return:向上滑动时,View也向上滑动并且消失,当向下滑动时,View马上出现.例如Google Now的搜索功能. ...
- MFC ADO连接Oracle12c数据库 类库文件
Stdafx.h Stdafx.h libado.h libado.h libado.cpp libado.cpp 参考网址如下:http://www.cnblogs.com/livewithnore ...
