??在軟件開發(fā)過程中,有時(shí)候我們會(huì)遇到需要把具體業(yè)務(wù)解耦出來美莫,讓各個(gè)模塊只負(fù)責(zé)相應(yīng)的處理邏輯.比如說菜拓,在我們的抽象業(yè)務(wù)邏輯中有一個(gè)負(fù)責(zé)開往目的地的司機(jī)driver,現(xiàn)在我們的業(yè)務(wù)系統(tǒng)中不僅要處理司機(jī)去往目的地的邏輯系宫,也有其他業(yè)務(wù)模塊需要關(guān)注司機(jī)具體的目的地是什么地方索昂。然而司機(jī)只負(fù)責(zé)把乘客送到具體地方。不需要負(fù)責(zé)告訴業(yè)務(wù)他去哪里扩借。這時(shí)候我們可以采用spring的事件機(jī)制椒惨,讓業(yè)務(wù)解耦。
spring的事件機(jī)制包括:
事件源潮罪,對(duì)應(yīng)具體的事件康谆。
事件監(jiān)聽器,負(fù)責(zé)接收具體的事件源嫉到。
事件廣播器沃暗, 負(fù)責(zé)發(fā)布事件源。
??相應(yīng)的處理邏輯為:事件監(jiān)聽器會(huì)注冊(cè)具體的事件源何恶,當(dāng)事件廣播器廣播具體的事件源后孽锥,事件監(jiān)聽器能接到該事件的發(fā)布消息,然后處理相關(guān)事件导而。
事件源
??定義事件源需要繼承spring的ApplicationEvent類忱叭,該類有兩子類ApplicationContextEvent,RequestHandledEvent.
??ApplicationContextEvent有4個(gè)子類今艺,ContextStartedEvent韵丑,ContextRefreshedEvent,ContextClosedEvent虚缎,ContextStoppedEvent撵彻,分別表示容器啟動(dòng)钓株,刷新,關(guān)閉陌僵,停止的事件轴合。
??RequestHandledEvent只有定義了DispatcherServlet時(shí)才會(huì)產(chǎn)生該事件,它的子類ServletRequestHandledEvent代表Servlet請(qǐng)求事件碗短。
??這里選擇定義一個(gè)司機(jī)行駛的事件類:DriverEvent受葛,它繼承于ApplicationContextEvent。它有一個(gè)目的地屬性destination偎谁,然后這里需要實(shí)現(xiàn)它的構(gòu)造函數(shù)总滩,提供一個(gè)spring的上下文。
package spring;
import lombok.Data;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.ApplicationContextEvent;
@Data
public class DriveEvent extends ApplicationContextEvent {
private String destination;
public DriveEvent(ApplicationContext source, String destination) {
super(source);
this.destination = destination;
}
}
事件監(jiān)聽器
??定于事件監(jiān)聽器需要實(shí)現(xiàn)ApplicationListener接口巡雨,我們定義一個(gè)DriveEventListener事件監(jiān)聽器闰渔,通過泛型注冊(cè)了它關(guān)注的事件DriveEvent,然后實(shí)現(xiàn)接口的onApplicationEvent(DriveEvent event)铐望,這里我們把事件的目的地打印出來冈涧。
package spring;
import org.springframework.context.ApplicationListener;
public class DriveEventListener implements ApplicationListener<DriveEvent> {
@Override
public void onApplicationEvent(DriveEvent event) {
System.out.println("本次哈哈列車開往目的地:" + event.getDestination());
}
}
事件廣播器
??接下來就需要定義事件廣播器了,事件的廣播器其實(shí)是通過spring的上下文ApplicationContext調(diào)用它的publishEvent(ApplicationEvent event)方法的正蛙,參數(shù)就是具體的繼承于ApplicationEvent的事件源督弓,這里我們定義一個(gè)Driver類,該類負(fù)責(zé)具體驅(qū)車前往目的地乒验,然后發(fā)布一個(gè)DriveEvent咽筋。
package spring;
import org.springframework.context.ApplicationContext;
public class Driver {
private ApplicationContext applicationContext = SpingContextHolder.getApplicationContext();
public void drive(String destionation) {
System.out.println("司機(jī)開車前往目的地");
DriveEvent driveEvent = new DriveEvent(applicationContext, destionation);
applicationContext.publishEvent(driveEvent);
}
}
??這里可以看到Driver有一個(gè)drive(String desctination)方法,它負(fù)責(zé)具體的驅(qū)車邏輯徊件,然后他會(huì)通過springcontext發(fā)布一個(gè)驅(qū)車事件,出于方便蒜危,我們?cè)俣x一個(gè)SpingContextHolder來獲取spring的上下文虱痕。
??在程序中獲取spring上下文可以通過指定具體的xml文件,也可以通過實(shí)現(xiàn)ApplicationContextAware 接口辐赞,然后實(shí)現(xiàn)它的setApplicationContext(ApplicationContext ctx)方法部翘,spring會(huì)傳遞當(dāng)先程序的上下文,我們給該類定義一個(gè)靜態(tài)的上下文變量context响委,把spring的上下文分配給它新思。代碼如下:
package spring;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class SpingContextHolder implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
context = ctx;
}
public static ApplicationContext getApplicationContext(){
return context;
}
}
啟動(dòng)類
??現(xiàn)在相應(yīng)的事件機(jī)制組件都已經(jīng)定義好了,我們這里再定義一個(gè)啟動(dòng)類赘风。
??啟動(dòng)類的邏輯流程為:我們?cè)趕pring的配置文件context.xml中裝配一個(gè)司機(jī)組件driver夹囚;程序啟動(dòng),通過ClassPathXmlApplicationContext來獲取司機(jī)的bean邀窃,司機(jī)driver調(diào)用drive方法驅(qū)車前往目的地荸哟。這時(shí)候自己的drive方法會(huì)發(fā)布DriveEvent事件,DriveEventListner會(huì)監(jiān)聽到然后打印出司機(jī)前往的目的地。這里司機(jī)只是在drive方法中發(fā)布一個(gè)驅(qū)車事件鞍历,然后驅(qū)車前往目的地舵抹,具體的處理邏輯通過事件機(jī)制交給了監(jiān)聽器處理邏輯,達(dá)到了程序解耦的效果劣砍。
package spring;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
Driver driver = (Driver) applicationContext.getBean("driver");
driver.drive("上海浦東");
}
}
spring配置文件:
<bean id="spingContextHolder" class="spring.SpingContextHolder"/>
<bean id="driver" class="spring.Driver"/>
<bean id="driveListener" class="spring.DriveEventListener"/>
總結(jié)
??我們通過繼承ApplicationEvent類來定義具體的事件惧蛹,然后實(shí)現(xiàn)ApplicationListener來注冊(cè)具體的事件以及相應(yīng)的處理邏輯,事件的廣播是通過spring的上下文ApplicationContext調(diào)用它的publishEvent(ApplicationEvent event)方法來實(shí)現(xiàn)的刑枝。當(dāng)事件廣播器廣播具體的事件源后香嗓,事件監(jiān)聽器能接到該事件的發(fā)布消息,然后處理相關(guān)事件仅讽。