ApplicationContext的事件機制是觀察者設計模式的實現,通過 ApplicationEvent 類和 ApplicationListener 接口,可以實現 ApplicationContext 的事件處理。如果容器中有一個 ApplicationListener Bean 每當 ApplicationContext 發布 ApplicationEvent時,ApplicationListener Bean將自動觸發。
Spring的事件框架有如下兩個重要成員。
》ApplicationEvent: 容器事件,必須由 ApplicationContext發布。
》ApplicationListener: 監聽器,可有容器中的任何監聽器Bean擔任
實際上,Spring 的事件機制與所有的事件機制都基本類似,他們都需要 事件源, 事件 和事件監聽器 組成。只是此處的事件源是ApplicationContext。
下圖簡單示范了ApplicationContext事件
下面的程序僵屍翻生Spring容器的事件程序 。程序先定義了一個ApplicationContext類,其對象就是一個Spring容器事件。代碼如下:
1 import org.springframework.context.ApplicationEvent; 2 3 public class EmailEvent extends ApplicationEvent{ 4 5 private static final long serialVersionUID = -7056841418193254583L; 6 7 private String address; 8 private String text; 9 public EmailEvent(Object source) { 10 super(source); 11 } 12 public String getAddress() { 13 return address; 14 } 15 public void setAddress(String address) { 16 this.address = address; 17 } 18 public String getText() { 19 return text; 20 } 21 public void setText(String text) { 22 this.text = text; 23 } 24 25 }
上面的EmailEvent類繼承了ApplicationContext類,除此之外,它就是一個普通的Java類。
容器的監聽器必須實現ApplicationListener接口,實現該接口必須實現如下方法:
》onApplicationEvent(ApplicationEvent event):每當容器內發生任何事件時,此方法都被觸發。
本例所用的監聽器代碼如下:
1 import org.springframework.context.ApplicationEvent; 2 import org.springframework.context.ApplicationListener; 3 4 public class EmailNotifier implements ApplicationListener<ApplicationEvent>{ 5 6 //該方法會在容器發生事件時自動觸發 7 public void onApplicationEvent(ApplicationEvent event) { 8 if(event instanceof EmailEvent){ 9 //只處理EmailEvent,發送Email通知... 10 EmailEvent emailEvent = (EmailEvent)event; 11 System.out.println("需要發送的郵件的接收地址:"+emailEvent.getAddress()); 12 System.out.println("需要發送郵件的郵件正文:"+emailEvent.getText()); 13 }else{ 14 //容器內置時間不做任何處理 15 System.out.println("容器本身事件: "+event); 16 } 17 } 18 19 }
將監聽器配置在容器中,配置文件如下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans 3 xmlns="http://www.springframework.org/schema/beans" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xmlns:p="http://www.springframework.org/schema/p" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> 7 <!-- 配置監聽器 --> 8 <bean class="com.spring_example.event.EmailNotifier" /> 9 </beans>
從上面的配置文件中可以看出,為Spring容器注冊事件監聽器,不需要像AWT編程那樣采用代碼進行編程,只要進行簡單的配置即可。當我們在Spring配置了一個實現ApplicationListener的Bean。Springl容器就會把這個Bean當初容器的監聽器。
當系統創建Spring容器,加載Spring容器時會自動觸發容器事件,容器事件監聽器可以監聽到這些事件。除此之外,程序也可調用ApplicationContext的publishEvent方法來主動觸發容器事件。如下主程序使用ApplicationContext的publishEvent來觸發事件。
1 ApplicationContext ac = new ClassPathXmlApplicationContext("/applicationContext.xml"); 2 //創建一個ApplicationEvent對象 3 EmailEvent ele = new EmailEvent("Hello"); 4 ele.setAddress("123456@163.com"); 5 ele.setText("Hello World"); 6 //主動觸發容器事件 7 ac.publishEvent(ele);
上面的代碼通過ApplicationContext對象的publishEvent主動觸發該事件。運行上面的程序將看到如下執行結果。
1 容器本身事件: org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.support.ClassPathXmlApplicationContext@141b571: startup date [Fri Sep 13 10:16:01 CST 2013]; root of context hierarchy] 2 需要發送的郵件的接收地址:123456@163.com 3 需要發送郵件的郵件正文:Hello World
從上面的執行結果可以看出,監聽器不僅監聽到程序所觸發的事件,也監聽到容器內置的事件。實際上,如果開發者需要在Spring容器初始化,銷毀時回調自定義方法,就可以通過上面的監聽器來實現。
如果Bean想發布事件,則Bean必須獲得其容器的引用。如果程序中沒有直接獲取容器的引用,則應該讓Bean實現ApplicationContextAware 或 BeanFactoryAware接口,從而可以獲得容器的引用。
Spring提供如下幾個內置事件:
》ContextRefreshEvent: ApplicationContext容器初始化或刷新觸發該事件。此處的初始化是指,所有Bean 被成功裝載,后處理Bean被檢測並激發,所有Singleton Bean被預實例化,ApplicationContext容器已就緒可用。
》ContextStartedEvent: 當使用ConfigurableApplicationContext(ApplicationContext的子接口)接口的start()方法啟動ApplicationContext容器時觸發該事件。容器管理生命周期的Bean實例將獲得一個指定的啟動信號,這在經常需要停止后重新啟動的場合比較常見。
》ContextClosedEvent:當使用ConfigurableApplicationContext接口的close()方法關閉ApplicationContext容器時觸發該事件。
》ContextStoppedEvent:當使用ConfigurableApplicationContext接口的stop()方法使ApplicationContext停止時觸發該事件。此處的“停止”意味着容器管理生命周期的Bean實例將獲得一個指定的停止信號,被停止的Spring容器可在此調用start()方法重新啟動。
》RequestHandledEvent:Web相關的事件,只能運用於使用DispatcherServlet的Web運用。在使用Spring作為前端的MVC控制器時,當Spring處理用戶請求結束后,系統會自動觸發該事件。
Spring的這種事件模型其實就是標准的觀察者設計模式。