天天看點

Spring 中的事件監聽的實作Spring 中的事件監聽的實作

Spring 中的事件監聽的實作

這裡我們不讨論事件監聽的機制的原理,我們隻讨論如何在項目中實作時間監聽。

Spring的事件監聽是基于觀察者模式。設計開發中。如下類與接口是我們必須要使用的。

ApplicationContext

首先我們了解一下ApplicationContext,還記得

ApplicationContext ac=new ClassPathXmlApplicationContext("beans.xml");
           

ApplicationContext相當于Spring的一個與IOC容器連接配接的橋梁,通過getBean();方法,我們可以輕松的從IOC容器中擷取Bean對象。

因為ApplicationContext是實作ApplicationEventPublisher的。檢視ApplicationEventPublisher的源碼,我們發現有一方法publishEvent。此方法便是釋出事件的方法,即觸發事件的方法,通過調用publishEvent方法,注入事件ApplicationEvent的子類,實作事件的觸發。

//這個是ApplicationContext類的聲明
public interface ApplicationContext extends EnvironmentCapable,ListableBeanFactory,HierarchicalBeanFactory,MessageSource, ApplicationEventPublisher, ResourcePatternResolver {//...}
           
//ApplicationEventPublisher源碼
public interface ApplicationEventPublisher {
    //該類隻有這一個方法,用于釋出通知,需要事件作為參數
    void publishEvent(ApplicationEvent event);

}
           

說了一大堆,就是想說ApplicationContext的

publicEvent(ApplicationEvent event);

方法是可以用來釋出通知,相當于觸發事件的事件源。

ApplicationContextAware

ApplicationContextAware類似于ServeletRequestAware,通過讓Action實作Aware,使得Action初始化之後便可以獲得一些資源,這裡我們讓Action實作ApplicationContext,使得Action擁有ApplicationContext,Action中擁有ApplicationContext之後就可以調用publicEvent方法進行通知

public interface ApplicationContextAware extends Aware {
    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;

}
           

ApplicationEvent

ApplicationEvent相當于一個事件,所有自定義事件都需要繼承這個抽象類。在Eclipse中Ctrl+Shift+H調用類的層次結構清單,可以看到如下

Spring 中的事件監聽的實作Spring 中的事件監聽的實作

Application下抽象子類ApplicationContextEvent的下面有4個已經實作好的事件

ContextClosedEvent(容器關閉時)

ContextRefreshedEvent(容器重新整理是)

ContextStartedEvent(容器啟動時候)

ContextStoppedEvent(容器停止的時候)

同樣,這四個事件都繼承了ApplicationEvent,如果我們想自定義事件,也可以通過繼承ApplicationEvent來實作

嗯,同樣是一句話總結ApplicationEvent就是一個抽象類,建立時間的時候隻需要繼承它就可以。

ApplicationListener

從名字可以看出來,這是一個監聽器。為什麼需要監聽器呢?監聽器是用于接收事件,并觸發事件的操作,這樣說起來可能有點費解,簡單的說就是,Listener是監聽ApplicationContext.publishEvent,方法的調用,一旦調用publishEvent,就會執行ApplicaitonListener中的方法,下面這個是ApplicationContext的源碼。

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

    /**
     * publishEvent觸發該方方法
     * 可以在該方法中寫各種業務邏輯
     */
    void onApplicationEvent(E event);

}
           

這裡是實際代碼實作的過程

  1. 建立一個MyEvent的類,繼承ApplicationEvent抽象類
package cn.blueboz.elec.event;

import org.springframework.context.ApplicationEvent;

public class MyEvent extends ApplicationEvent {
    //存放構造器送入的值
    private String msg;
    //構造器參數可以随意設定,這裡為了友善調試,設定為字元串
    public MyEvent(String msg) {
        super(msg);
        this.msg=msg;
    }
    //自定義一個方法,這個方法也可以随意寫,這裡也是測試用
    public void myevent(){
        System.out.println("********My event**************");
        System.out.println(msg);
        System.out.println("*******************************");
    }
}
           

2.建立一個監聽器MyListener

package cn.blueboz.elec.listener;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.ContextStartedEvent;
import org.springframework.context.event.ContextStoppedEvent;
import org.springframework.stereotype.Service;

import cn.blueboz.elec.event.HisEvent;
import cn.blueboz.elec.event.MyEvent;

//注入IOC容器中
@Service("myListener")
public class MyListener implements ApplicationListener<ApplicationEvent> {
    //調用ApplicationContext.publishEvent方法時會觸發執行該方法
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        //判斷事件為MyEvent時候執行
        if(event instanceof MyEvent){
            //強制轉換
            MyEvent evt=(MyEvent) event;
            //執行自定義事件中的自定義方法
            evt.myevent();
        }
        //如果容器關閉時,觸發
        if(event instanceof ContextClosedEvent){
            ContextClosedEvent cce=(ContextClosedEvent) event;
            System.out.println("#####################");
            System.out.println("容器關閉");
            System.out.println(cce);
            System.out.println("#####################");
        }
        //容器重新整理時候觸發
        if(event instanceof ContextRefreshedEvent){
            ContextRefreshedEvent cre=(ContextRefreshedEvent) event;
            System.out.println("#####################");
            System.out.println("容器重新整理");
            System.out.println(cre);
            System.out.println("#####################");
        }
        //容器啟動的時候觸發
        if(event instanceof ContextStartedEvent){
            ContextStartedEvent cse=(ContextStartedEvent) event;
            System.out.println("#####################");
            System.out.println("容器啟動");
            System.out.println(cse);
            System.out.println("#####################");
        }
        //容器停止時候觸發
        if(event instanceof ContextStoppedEvent){
            ContextStoppedEvent cse=(ContextStoppedEvent) event;
            System.out.println("#####################");
            System.out.println("容器停止");
            System.out.println(cse);
            System.out.println("#####################");
        }
    }

}
           

3.最後,我們要再Action中釋出通知publishEvent;

package cn.blueboz.elec.web.action;

import javax.annotation.Resource;

import org.apache.struts2.interceptor.ServletRequestAware;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Scope;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Controller;

import cn.blueboz.elec.domain.ElecText;
import cn.blueboz.elec.event.MyEvent;
import cn.blueboz.elec.service.IElecTextService;

//指定為prototype原型,對應每一個請求都會産生一個執行個體對象
@Controller("elecTextAction")
@Scope(value="prototype")
public class ElecTextAction extends BaseAction<ElecText> implements ApplicationContextAware,ServletRequestAware {
    //首先獲得模型驅動對象

    ElecText elecText=getModel();
    protected ApplicationContext applicationContext;
    //注入Service指定從Spring的IOC容器中注入的對象的名稱
    @Resource(name=IElecTextService.SERVICE_NAME)
    private IElecTextService elecTextService;

    public String save(){
        //從表單中傳送過來的執行個體對象
        elecTextService.saveElecText(elecText);
        /**
         * 請關注這一行代碼,在頁面中通路時候調用save方法
         * save方法中執行了publishEvent方法釋出通知。
         * 傳入參數是自定義事件MyEvent
         */
        applicationContext.publishEvent(new MyEvent("在Action中的save方法Public了Event"));
        return "save";
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        this.applicationContext=applicationContext;
    }
}
           

4.啟動Tomcat時候指令行輸出

#####################
容器重新整理
org.springframework.context.event.ContextRefreshedEvent[source=Root WebApplicationContext: startup date [Fri Nov 20 17:12:47 CST 2015]; root of context hierarchy]
#####################
           

通路頁面的時候,指令行輸出,可以看出,觸發了MyEvent方法輸出。

********My event**************
在Action中的save方法Public了Event
*******************************