程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> spring事件通知機制詳解,spring事件詳解

spring事件通知機制詳解,spring事件詳解

編輯:JAVA綜合教程

spring事件通知機制詳解,spring事件詳解


優勢

  • 解耦
  • 對同一種事件有多種處理方式
  • 不干擾主線(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.ClassPathXmlApplicationContext; 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 ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml"); 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 @Override 29 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 @Component 13 public class AllAcceptedListener implements ApplicationListener<ApplicationEvent> { 14 @Override 15 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 @Component 13 public class CustomizePublisher implements ApplicationEventPublisherAware { 14 15 private ApplicationEventPublisher applicationEventPublisher; 16 17 public void publish(MealEvent event) { 18 applicationEventPublisher.publishEvent(event); 19 } 20 21 @Override 22 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 @Component 12 public class MealListener implements ApplicationListener<MealEvent> { 13 @Override 14 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 mealContent 16 * 吃什麼 17 * @param mealEnum 18 * 早餐還是午餐? 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 dinner 12 } 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 @Component 12 public class TroubleListener implements ApplicationListener<TroubleEvent> { 13 @Override 14 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 @Override 15 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 down 26          } 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 @Override
2 public void start() {
3    getLifecycleProcessor().start();
4    publishEvent(new ContextStartedEvent(this));
5 }

ContextStoppedEvent:當ApplicationContext stop時觸發該事件; 

1 @Override
2 public void stop() {
3    getLifecycleProcessor().stop();
4    publishEvent(new ContextStoppedEvent(this));
5 }

ApplicationListener 

依賴關系

帶你一步步走向源碼的世界

從上邊打印的線程信息可以知道,spring處理事件通知采用的是當前線程,並沒有為為我們啟動新的線程,所以,如果需要,你要自己處理線程信息哦,當然也可以設定(如何設置?)!

AbstractApplicationContext

補齊:同一個event,被多個listener監聽,先被哪個listener執行是由下邊的代碼決定的:

如何設置線程池?

回到上邊的問題,到底該如何設置線程池呢?

AbstractApplicationEventMulticaster 是private的,並且沒有提供寫入方法...

實際案例

用在自己的代碼裡就是最好的例子了 ^_^

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved