优势

  • 解耦
  • 对同一种事件有多种处理方式
  • 不干扰主线(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事件通知机制详解的更多相关文章

  1. Android事件分发机制详解

    事件分发机制详解 一.基础知识介绍 1.经常用的事件有:MotionEvent.ACTION_DOWN,MotionEvent.ACTION_MOVE,MotionEvent.ACTION_UP等 2 ...

  2. Android事件传递机制详解及最新源码分析——ViewGroup篇

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 在上一篇<Android事件传递机制详解及最新源码分析--View篇>中,详细讲解了View事件的传递机制,没掌握或者掌握不扎实的小伙伴 ...

  3. Android 的事件传递机制,详解

    Android 的事件传递机制,详解 前两天和一个朋友聊天的时候.然后说到事件传递机制.然后让我说的时候,忽然发现说的不是非常清楚,事实上Android 的事件传递机制也是知道一些,可是感觉自己知道的 ...

  4. Android Touch事件传递机制详解 下

    尊重原创:http://blog.csdn.net/yuanzeyao/article/details/38025165 资源下载:http://download.csdn.net/detail/yu ...

  5. Android开发——事件分发机制详解

    0. 前言   转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/52566965 深入学习事件分发机制,是为了解决在Android开发中 ...

  6. IOS 触摸事件分发机制详解

    欢迎大家前往云+社区,获取更多腾讯海量技术实践干货哦~ 作者:MelonTeam 前言 很多时候大家都不关心IOS触摸事件的分发机制的实现原理,当遇到以下几种情形的时候你很可能抓破头皮都找不到解决方案 ...

  7. 【Android面试查漏补缺】之事件分发机制详解

    前言 查漏补缺,查漏补缺,你不知道哪里漏了,怎么补缺呢?本文属于[Android面试查漏补缺]系列文章第一篇,持续更新中,感兴趣的朋友可以[关注+收藏]哦~ 本系列文章是对自己的前段时间面试经历的总结 ...

  8. android 事件分发机制详解(OnTouchListener,OnClick)

    昨天做东西做到触摸事件冲突,以前也经常碰到事件冲突,想到要研究一下Android的事件冲突机制,于是从昨天开始到今天整整一天时间都要了解这方面的知识,这才懂了安卓的触摸和点击事件的机制.探究如下: 首 ...

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

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

随机推荐

  1. Android Studio 经常使用功能介绍

    为了简化 Android 的开发力度,Google 决定将重点建设 Android Studio 工具.Google 会在今年年底停止支持其它集成开发环境.比方 Eclipse. Android St ...

  2. CvMat 矩阵的使用方法和简单程序

    一:CvMat* cvInitMatHeader( CvMat* mat, int rows, int cols, int type,void* data=NULL, int step=CV_AUTO ...

  3. HTML5图片旋转

    HTML5图片旋转 首先我们使用Cococs2dx-Js-Lite版,来创建一个工程,我们所需要的开发环境如下: 1,webstrom 2,google chrome浏览器 3,cocos2dx-Js ...

  4. [leetode]Binary Search Tree Iterator

    用个stack模拟递归即可 /** * Definition for binary tree * struct TreeNode { * int val; * TreeNode *left; * Tr ...

  5. uniGUI试用笔记(十五)通过URL控制参数

    通过URL代入参数,在代码中读取,如: http://localhost:8501/?ServerPort=212&&ServerIP=192.168.31.12 在代码中可以通过: ...

  6. 关于Linux session管理与GUI架构

    google了一下感觉一下子找不到太好的资料,可能需要慢慢深入去学习. 这里有一个讲session management的,还算比较深入: https://dvdhrm.wordpress.com/2 ...

  7. [Z]CS权威会议

    CS Conference TOP 40 计算机会议TOP40 一.A 类 15 个 ASPLOS: Architecture Support for Programming Languages an ...

  8. DevExpress.XtraGrid winform试用分享

    DevExpress.XtraGrid在winform里使用还挺麻烦,为了减少重复代码和代码复用,本人针对DevExpress.XtraGrid封装了一个Form的基类,其中涵盖了DevExpress ...

  9. 一个程序说明标准i/O缓冲的效率

    来瞧一个程序,了解一下标准I/O中的缓冲区对程序运行的影响: #include<stdio.h>#include<unistd.h> int main(){ char a[2] ...

  10. android手势创建及识别

    使用一些浏览器或者输入法应用时会有一些手势操作,还可以自定义手势.这些神奇的操作是怎么做的呢?这一篇重点记录手势的识别和创建.这篇的内容使用到的是android.gesture包,具体的例子参考的是S ...