优势
- 解耦
- 对同一种事件有多种处理方式
- 不干扰主线(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.ClassPath 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 ClassPathnew ClassPath);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 @Override29 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
测试结果
测试成员
成员代码
接受全部事件的演员(很负责任啊)
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 @Component13 public class AllAcceptedListener implements ApplicationListener<ApplicationEvent> {14 @Override15 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 @Component13 public class CustomizePublisher implements ApplicationEventPublisherAware {14 15 private ApplicationEventPublisher applicationEventPublisher;16 17 public void publish(MealEvent event) {18 applicationEventPublisher.publishEvent(event);19 }20 21 @Override22 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 @Component12 public class MealListener implements ApplicationListener<MealEvent> {13 @Override14 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 mealContent16 * 吃什么17 * @param mealEnum18 * 早餐还是午餐?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 dinner12 }
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 @Component12 public class TroubleListener implements ApplicationListener<TroubleEvent> {13 @Override14 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 @Override15 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 down26 }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 @Override2 public void start() {3 getLifecycleProcessor().start();4 publishEvent(new ContextStartedEvent(this));5 }
ContextStoppedEvent:当ApplicationContext stop时触发该事件;
1 @Override2 public void stop() {3 getLifecycleProcessor().stop();4 publishEvent(new ContextStoppedEvent(this));5 }
ApplicationListener
依赖关系
带你一步步走向源码的世界
从上边打印的线程信息可以知道,spring处理事件通知采用的是当前线程,并没有为为我们启动新的线程,所以,如果需要,你要自己处理线程信息哦,当然也可以设定(如何设置?)!
AbstractApplicationContext
补齐:同一个event,被多个listener监听,先被哪个listener执行是由下边的代码决定的:
如何设置线程池?
回到上边的问题,到底该如何设置线程池呢?
AbstractApplicationEventMulticaster 是private的,并且没有提供写入方法...
实际案例
用在自己的代码里就是最好的例子了 ^_^
原标题:spring事件通知机制详解
关键词:Spring