成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

扒去Spring事件監聽機制的外衣,竟然是觀察者模式

開發 前端
本篇文章帶大家從觀察者模式、Java事件機制延伸到Spring的事件監聽機制,將三者融合在一起來講解。通過這個案例,其實我們能夠體會到一些經驗性的知識,比如看似復雜的Spring事件監聽機制實現只不過是觀察者模式的一種實現,而其中又集成了Java的事件機制。這也就是所謂的融會貫通。

前言

Spring中提供了一套默認的事件監聽機制,在容器初始化時便使用了這套機制。同時,Spring也提供了事件監聽機制的接口擴展能力,開發者基于此可快速實現自定義的事件監聽功能。

Spring的事件監聽機制是在JDK事件監聽的基礎上進行的擴展,也是在典型觀察者模式上的進一步抽象和改進。所以,結合Spring的事件監聽機制與觀察者模式來學習,可以達到理論與實踐的完美融合。

本篇文章就以觀察者模式和Spring事件監聽機制作為切入點,結合具體的實例來對兩者進行系統的學習和實踐。

觀察者模式

觀察者模式(Observer Pattern),也叫作發布-訂閱模式(Publish/Subscribe)。

無論是觀察者模式,還是Spring的事件監聽機制,本質上都是在定義對象間一對多的依賴關系,使得每當一個對象(被觀察者/事件)改變狀態時,所有依賴于它的對象(觀察者/事件監聽器)都會得到通知,并被自動更新。

觀察者模式的優點在于:觀察者和被觀察者之間是抽象耦合,不管是新增觀察者或是被觀察者,都非常容易擴展。這也符合面向對象所倡導的“開閉原則”:對擴展開放,對修改關閉。

觀察者模式適用于以下三類場景:

  • 關聯行為場景,而且關聯是可拆分的。
  • 事件多級觸發場景。
  • 跨系統的消息交換場景,比如消息隊列的處理機制。

在使用的過程中,也要綜合考慮開發效率和運行效率的問題。通常,一個被觀察者會對應多個觀察者,那么在開發和調試的過程中會有一定的復雜度。

同時,因為被觀察者存在關聯、多級拆分,也就是會有多個觀察者,而Java消息的通知(和Spring的事件監聽機制)默認是順序執行的,如果其中一個觀察者執行時間過長或卡死,勢必會影響整體的效率。此時,就需要考慮異步處理。

觀察者的角色定義

觀察者模式是一個典型的發布-訂閱模型,其中主要涉及四個角色:

  • 抽象被觀察者角色:內部持有所有觀察者角色的引用,并對外提供新增、移除觀察者角色、通知所有觀察者的功能;
  • 具體被觀察者角色:當狀態變更時,會通知到所有的觀察者角色;
  • 抽象觀察者角色:抽象具體觀察者角色的一些共性方法,如狀態變更方法;
  • 具體觀察者角色:實現抽象觀察者角色的方法;

UML類圖展示類觀察者模式大體如下:

圖片

觀察者模式類圖

以具體的代碼來展示一下觀察者模式的實現。

第一,定義抽象觀察者。

/**
* 抽象觀察者角色
* @author sec
**/
public abstract class AbstractObserver {

/**
* 接收消息
* @param context 消息內容
*/
public abstract void receiveMsg(String context);

}

第二,定義抽象被觀察者。

/**
* 抽象主題(抽象被觀察者角色)
* @author sec
**/
public abstract class AbstractSubject {

/**
* 持有所有抽象觀察者角色的集合引用
*/
private final List<AbstractObserver> observers = new ArrayList<>();

/**
* 添加一個觀察者
* @param observer 觀察者
*/
public void addObserver(AbstractObserver observer){
observers.add(observer);
}

/**
* 移除一個觀察者
* @param observer 觀察者
*/
public void removeObserver(AbstractObserver observer){
observers.remove(observer);
}

/**
* 通知所有的觀察者,執行觀察者更新方法
* @param context 通知內容
*/
public void notifyObserver(String context){
observers.forEach(observer -> observer.receiveMsg(context));
}
}

第三,定義具體被觀察者,實現了抽象被觀察者。

/**
* 具體被觀察者
* @author sec
**/
public class ConcreteSubject extends AbstractSubject{

/**
* 被觀察者發送消息
* @param context 消息內容
*/
public void sendMsg(String context){
System.out.println("具體被觀察者角色發送消息: " + context);
super.notifyObserver(context);
}
}

第四,定義具體觀察者,實現了抽象觀察者。

/**
* 具體觀察者角色實現類
* @author sec
**/
public class ConcreteObserver extends AbstractObserver{

@Override
public void receiveMsg(String context) {
System.out.println("具體觀察者角色接收消息: " + context);
}
}

第五,使用演示類。

public class ObserverPatternTest {

public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
subject.addObserver(new ConcreteObserver());
subject.sendMsg("Hello World!");
}
}

執行上述方法,控制臺打印日志為:

具體被觀察者角色發送消息: Hello World!
具體觀察者角色接收消息: Hello World!

在上述代碼實現中,被觀察者發出消息后,觀察者接收到具體的消息,如果添加了多個觀察者,它們均會收到消息。也就是前面所說的,每當一個對象(被觀察者/事件)改變狀態時,所有依賴于它的對象(觀察者/事件監聽器)都會得到通知,并被自動更新。

Java中的事件機制

前面聊了觀察者模式,這里再來看看Java中的事件機制。

在JDK 1.1及以后版本中,事件處理模型采用基于觀察者模式的委派事件模型(DelegationEvent Model, DEM),即一個Java組件所引發的事件并不由引發事件的對象自己來負責處理,而是委派給獨立的事件處理對象負責。

這并不是說事件模型是基于Observer和Observable的,事件模型與Observer和Observable沒有任何關系,Observer和Observable只是觀察者模式的一種實現而已。

Java中的事件機制有三個角色參與:

  • Event Source:事件源,發起事件的主體。
  • Event Object:事件狀態對象,傳遞的信息載體,可以是事件源本身,一般作為參數存在于listerner的方法之中。所有事件狀態對象都將從Java中的EventObject派生而來;
  • Event Listener:事件監聽器,當監聽到EventObject產生時,調用相應的方法進行處理。所有事件偵聽器接口必須擴展EventListener接口;

UML類圖展示類事件模式大體如下:

圖片

事件監聽

在上面的UML圖中,EventObject一般作為Listener處理方法的參數傳入,而EventSource是事件的觸發者,通過此對象注冊相關的Listener,然后向Listener觸發事件。

通過UML圖的對比可以看出,事件監聽模式和觀察者模式大同小異,它們屬于同一類型模式,都屬于回調機制,主動推送消息,但在使用場景上有所區別。

觀察者(Observer)相當于事件監聽者(監聽器),被觀察者(Observable)相當于事件源和事件,事件監聽比觀察者模式要復雜一些,多了EventSource角色的存在。

以具體的代碼來展示一下Java中的事件機制實現。

第一,定義事件對象。

/**
* 事件對象
*
* @author sec
**/
public class DoorEvent extends EventObject {

private int state;

/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred.
* @throws IllegalArgumentException if source is null.
*/
public DoorEvent(Object source) {
super(source);
}

public DoorEvent(Object source, int state) {
super(source);
this.state = state;
}

// 省略getter/setter方法
}

第二,定義事件監聽器接口。

/**
* 事件監聽器接口
*
* @author sec
**/
public interface DoorListener extends EventListener {

/**
* 門處理事件
* @param doorEvent 事件
*/
void doorEvent(DoorEvent doorEvent);
}

第三,定義事件監聽器的實現類。

public class CloseDoorListener implements DoorListener{
@Override
public void doorEvent(DoorEvent doorEvent) {
if(doorEvent.getState() == -1){
System.out.println("門關上了");
}
}
}

public class OpenDoorListener implements DoorListener{
@Override
public void doorEvent(DoorEvent doorEvent) {
if(doorEvent.getState() == 1){
System.out.println("門打開了");
}
}
}

這里實現了門的開和關兩個事件監聽器類。

第四,定義事件源EventSource。

public class EventSource {

//監聽器列表,監聽器的注冊則加入此列表
private Vector<DoorListener> listenerList = new Vector<>();

//注冊監聽器
public void addListener(DoorListener eventListener) {
listenerList.add(eventListener);
}

//撤銷注冊
public void removeListener(DoorListener eventListener) {
listenerList.remove(eventListener);
}

//接受外部事件
public void notifyListenerEvents(DoorEvent event) {
for (DoorListener eventListener : listenerList) {
eventListener.doorEvent(event);
}
}
}

第五,測試類。

public class EventTest {

public static void main(String[] args) {
EventSource eventSource = new EventSource();
eventSource.addListener(new CloseDoorListener());
eventSource.addListener(new OpenDoorListener());

eventSource.notifyListenerEvents(new DoorEvent("關門事件", -1));
eventSource.notifyListenerEvents(new DoorEvent("開門時間", 1));
}
}

執行測試類,控制臺打印:

門關上了
門打開了

事件成功觸發。

Spring中的事件機制

在了解了觀察者模式和Java的事件機制之后,再來看看Spring中的事件機制。在Spring容器中,通過ApplicationEvent和ApplicationListener接口來實現事件監聽機制。每次Event事件被發布到Spring容器中,都會通知對應的Listener。默認情況下,Spring的事件監聽機制是同步的。

Spring的事件監聽由三部分組成:

  • 事件(ApplicationEvent):該類繼承自JDK中的EventObject,負責對應相應的監聽器,事件源發生某事件是特定事件監聽器被觸發的原因;
  • 監聽器(ApplicationListener):該類繼承自JDK中的EventListener,對應于觀察者模式中的觀察者。監聽器監聽特定事件,并在內部定義了事件發生后的響應邏輯;
  • 事件發布器(ApplicationEventPublisher):對應于觀察者模式中的被觀察者/主題,負責通知觀察者,對外提供發布事件和增刪事件監聽器的接口,維護事件和事件監聽器之間的映射關系,并在事件發生時負責通知相關監聽器。

通過上面的分析可以看出Spring的事件機制不僅是觀察者模式的一種實現,也實現了JDK提供的事件接口。同時,除了發布者和監聽者之外,還存在一個EventMulticaster的角色,負責把事件轉發給監聽者。

Spring事件機制的工作流程如下:

圖片

Spring事件機制

在上述流程中,發布者調用applicationEventPublisher.publishEvent(msg),將事件發送給EventMultiCaster。EventMultiCaster注冊著所有的Listener,它會根據事件類型決定轉發給那個Listener。

在Spring中提供了一些標準的事件,比如:ContextRefreshEvent、ContextStartedEvent、ContextStoppedEvent、ContextClosedEvent、RequestHandledEvent等。

關于Spring事件機制的具體實現和這些標準事件的作用,大家可以通過閱讀源碼來學習,這里不再詳細展開。

下面來看看Spring事件機制涉及到的幾個角色的源碼及后續基于它們的實踐。

第一,事件(ApplicationEvent)。

public abstract class ApplicationEvent extends EventObject {

/** use serialVersionUID from Spring 1.2 for interoperability. */
private static final long serialVersionUID = 7099057708183571937L;

/** System time when the event happened. */
private final long timestamp;

/**
* Create a new {@code ApplicationEvent}.
* @param source the object on which the event initially occurred or with
* which the event is associated (never {@code null})
*/
public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}

/**
* Return the system time in milliseconds when the event occurred.
*/
public final long getTimestamp() {
return this.timestamp;
}

}

事件可類比觀察者中的被觀察者實現類的角色,繼承自JDK的EventObject。上述Spring中的標準事件都是直接或間接繼承自該類。

第二,事件發布器(ApplicationEventPublisher)。

@FunctionalInterface
public interface ApplicationEventPublisher {

default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
}

void publishEvent(Object event);
}

通過實現ApplicationEventPublisher接口,并重寫publishEvent()方法,可以自定義事件發布的邏輯。ApplicationContext繼承了ApplicationEventPublisher接口。因此,我們可以通過實現ApplicationContextAware接口,注入ApplicationContext,然后通過ApplicationContext的publishEvent()方法來實現事件發布功能。

ApplicationContext容器本身僅僅是對外提供了事件發布的接口publishEvent(),真正的工作委托給了具體容器內部的ApplicationEventMulticaster對象。而ApplicationEventMulticaster對象可類比觀察者模式中的抽象被觀察者角色,負責持有所有觀察者集合的引用、動態添加、移除觀察者角色。

第三,事件監聽器(ApplicationListener)。

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

/**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event);

}

事件監聽器(ApplicationListener)對應于觀察者模式中的具體觀察者角色。當事件發布之后,就會執行事件監聽器的邏輯。通過實現ApplicationListener接口,并重寫onApplicationEvent()方法,就可以監聽到事件發布器發布的事件。

Spring事件監聽案例

下面以具體的案例代碼來說明如何自定義實現Spring事件監聽。

第一,自定義定義事件對象,集成自ApplicationEvent。

public class MyEvent extends ApplicationEvent {
/**
* Create a new {@code ApplicationEvent}.
*
* @param source the object on which the event initially occurred or with
* which the event is associated (never {@code null})
*/
public MyEvent(Object source) {
super(source);
}

private String context;

public MyEvent(Object source, String context){
super(source);
this.context = context;
}

public String getContext() {
return context;
}

public void setContext(String context) {
this.context = context;
}
}

第二,自定義ApplicationListener事件監聽器。

@Component
public class MyApplicationListener implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent event) {
// 監聽到具體事件,處理對應具體邏輯
System.out.println("event.getContext() = " + event.getContext());
}

}

除了上述基于實現ApplicationListener接口的方式外,還可以使用**@EventListener**注解來實現,實現示例如下:

@Component
public class MyApplicationListener{

// 通過注解實現監聽器
@EventListener
public void handleMyEvent(MyEvent event){
// 監聽到具體事件,處理對應具體邏輯
System.out.println("event.getContext() = " + event.getContext());
}
}

第三,使用及單元測試。

@Slf4j
@SpringBootTest
public class SpringEventTest {

@Autowired
private ApplicationEventPublisher eventPublisher;

@Test
void testEvent() {
eventPublisher.publishEvent(new MyEvent("自定義事件", "Hello World!"));
}
}

執行單元測試,可看到控制臺打印對應的事件信息。

通過上述方式我們已經成功實現了基于Spring的事件監聽機制,但這其中還有一個問題:同步處理。默認情況下,上述事件是基于同步處理的,如果其中一個監聽器阻塞,那么整個線程將處于等待狀態。

那么,如何使用異步方式處理監聽事件呢?只需兩步即可。

第一步,在監聽器類或方法上添加@Async注解,例如:

@Component
@Async
public class MyApplicationListener implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent event) {
// 監聽到具體事件,處理對應具體邏輯
System.out.println("event.getContext() = " + event.getContext());
}

}

第二步,在SpringBoot啟動類(這里以SpringBoot項目為例)上添加@EnableAsync注解,例如:

@SpringBootApplication
@EnableAsync
public class SpringBootMainApplication {

public static void main(String[] args) {
SpringApplication.run(SpringBootMainApplication.class, args);
}
}

此時,就可以實現異步監聽功能了。當然,@Async注解也可以指定我們已經配置好的線程池來處理異步請求,關于線程數的初始化這里就不再演示了。

小結

本篇文章帶大家從觀察者模式、Java事件機制延伸到Spring的事件監聽機制,將三者融合在一起來講解。通過這個案例,其實我們能夠體會到一些經驗性的知識,比如看似復雜的Spring事件監聽機制實現只不過是觀察者模式的一種實現,而其中又集成了Java的事件機制。這也就是所謂的融會貫通。

我們如果單純的學習某一個設計模式,可能只會運用和識別它的簡單實現,而實踐中往往會對設計模式進行變種,甚至融合多種設計模式的優點于一體,這便是活學活用。希望通過這邊文章你能夠更加深入的理解上述三者。

參考文章:

??https://blog.csdn.net/Weixiaohuai/article/details/122367792??

??https://www.cnblogs.com/admol/p/14036564.html??

??https://blog.csdn.net/qq_30364247/article/details/123168800??

??https://cloud.tencent.com/developer/article/1701947??

??https://www.jianshu.com/p/81a8ca9a6ffa??

責任編輯:武曉燕 來源: 程序新視界
相關推薦

2024-12-03 09:34:35

觀察者模 式編程Javav

2021-03-29 07:14:28

Spring觀察者模式

2020-10-26 08:45:39

觀察者模式

2021-09-06 10:04:47

觀察者模式應用

2022-01-29 22:12:35

前端模式觀察者

2013-11-26 17:09:57

Android設計模式

2021-07-08 11:28:43

觀察者模式設計

2011-04-29 09:22:22

2021-07-28 06:51:08

Nacos代理模式

2021-07-08 07:52:49

容器監聽器Event

2012-08-27 10:52:20

.NET架構觀察者模式

2021-04-14 14:40:37

forSpringJava

2021-06-07 20:03:04

監聽器模式觀察者

2022-07-13 08:36:57

MQ架構設計模式

2024-02-18 12:36:09

2015-11-25 11:10:45

Javascript設計觀察

2009-03-30 09:39:04

觀察者思想換位設計模式

2024-06-04 13:11:52

Python行為設計模式開發

2021-08-05 06:54:05

觀察者訂閱設計

2021-01-25 05:38:04

設計原理VueSubject
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日韩欧美国产一区二区 | 色综合av| 福利在线观看 | 亚洲天堂二区 | 国产激情视频在线观看 | 精品无码久久久久久国产 | 国产精品亚洲成在人线 | 超碰av人人 | 人妖无码 | 亚洲一区二区三区免费视频 | 中文字幕国产一区 | 91福利在线观看视频 | 国产亚洲高清视频 | 日韩欧美国产不卡 | 国产一区二区视频免费在线观看 | 色妞av| 天天久 | 国产精品亚洲一区 | 国产精品99久久久久久宅男 | 久久精品一区二区 | 婷婷久久久久 | 中午字幕在线观看 | 日韩免费毛片视频 | 龙珠z国语版在线观看 | 成人不卡 | 精品视频在线免费观看 | 91久久精品国产91久久 | 91精品国产综合久久久久久蜜臀 | 二区av| 久久小视频 | 亚洲精品视频播放 | 男人的天堂久久 | 久久精品无码一区二区三区 | 情侣酒店偷拍一区二区在线播放 | 国产不卡在线观看 | 成人二区 | 日韩精品一区二区三区中文在线 | 欧美久久影院 | 日韩中文字幕一区二区 | 国产精品波多野结衣 | 久久久欧洲 |