优势

  • 解耦
  • 对同一种事件有多种处理方式
  • 不干扰主线(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. NSObject的load和initialize方法(转)

    全文转载自:http://www.cocoachina.com/ios/20150104/10826.html 在Objective-C中,NSObject是根类,而NSObject.h的头文件中前两 ...

  2. 64位Linux下编译搭建Nginx1.5与PHP5.5(CentOS6.4)

    (1)安装Nginx1.5.2更新Nginx和PHP的依赖包yum -y install gcc gcc-c++ autoconf libjpeg libjpeg-devel libpng \libp ...

  3. 更新jar包中的MANIFEST.MF

    当前目录存在lib.jar和MANIFEST.MF文件,在当前目录下执行: jar -uvfm lib.jar MANIFEST.MF 如果lib.jar里存在META-INF/MANIFEST.MF ...

  4. OceanBase server处理网络包的回调逻辑

    OceanBase处理网络包的逻辑还是蛮绕的,这里以UPS为例,作为给自己的备忘. UPS代码的main.cpp中调用ObUpdateServerMain的start启动server.start函数会 ...

  5. 构建基于WinRT的WP8.1 App 02:数据绑定新特性

    基于WinRT的Windows Phone 8.1以及Windows 8.1中Xaml数据绑定增加了一些新特性. FallBackValue属性:FallBackValue在绑定的值属性值不存在时,可 ...

  6. 在seajs中使用require加载静态文件的问题

    注意,在seajs中使用require加载静态文件时,必须使用常量,不能用变量.如果一定要用变量,请使用require.async var html = require("view/sys/ ...

  7. wireshark使用笔记

    tcp && (ip.src==xxx.xxx.xxx.xxx || ip.dst==yyy.yyy.yyy.yyy)

  8. 让我们一起Go(十一)

    前言: 今天又要继续了,当初自己的挖的坑必须得填啊,尽管天气非常滴热,但是丝毫无法阻挡我填坑的热情,那么,我们继续让我们一起Go!!! 定义方法: 这里我们要来看看Golang中的(Methods)方 ...

  9. Making Your ActionBar Not Boring

    这篇文章转自国外一个技术大牛的博客,首先感谢这位大牛的无私奉献. Android应用中有一名位 Google书报摊的应用,他实现了一种新的ActionBar风格.当用户初始进入该界面的时候,为一个透明 ...

  10. 网络通信分享(二):外网ip和内网ip

    一.内网ip包括两类: 1:tcp/ip协议中,专门保留了三个IP地址区域作为私有地址,其地址范围如下: 10.0.0.0/8:10.0.0.0-10.255.255.255  172.16.0.0/ ...