上一篇:JDK 自帶的觀察者模式就很香!
前段時間棧長給大家分享了什麼是觀察者模式,以及在 JDK 中如何實現觀察者模式,現在都是 Spring 的天下了,今天就再分享下如何在 Spring/ Spring Boot 中實現觀察者模式。
不用再面試 for 迴圈程式設計了,Spring 框架自帶的事件監聽機制,實現觀察者模式、實現解耦輕鬆幫你全搞定!
Spring 事件監聽機制
其實在 Spring/ Spring Boot 框架中有一套事件監聽機制,可以實現觀察者模式。
Spring/ Spring Boot 框架中也都內建了許多事件,我們也可以自定義釋出應用程式事件,下面我們會介紹。
其主要涉及到的幾個核心類和介面如下 :
ApplicationEvent
ApplicationEvent(應用程式事件)它是一個抽象類,相當於觀察者模式中的觀察目標。
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;
}
}
ApplicationEvent 繼承自 Java 中的 EventObject 事件物件類,Spring 框架中的所有事件都繼承自 ApplicationEvent 類,它是所有事件的父類。
ApplicationEvent 主要的核心是類構造器,它可以初始化一個 source 事件關聯物件,以便在事件監聽器中獲取並通知更新。
ApplicationListener
ApplicationListener(應用程式事件監聽器)它是一個介面,相當於觀察者模式中的觀察者。
ApplicationListener 原始碼如下:
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event);
}
ApplicationListener 繼承自 Java 中的 EventListener 事件監聽介面,ApplicationListener 類中只有一個 onApplicationEvent 方法,當指定監聽的事件被髮布後就會被觸發執行,可以通過 event 獲取事件中的關聯物件。
ApplicationEventPublisher
應用程式事件釋出介面,封裝了事件釋出功能的基礎介面。
public interface ApplicationEventPublisher {
/**
* Notify all <strong>matching</strong> listeners registered with this
* application of an application event. Events may be framework events
* (such as ContextRefreshedEvent) or application-specific events.
* <p>Such an event publication step is effectively a hand-off to the
* multicaster and does not imply synchronous/asynchronous execution
* or even immediate execution at all. Event listeners are encouraged
* to be as efficient as possible, individually using asynchronous
* execution for longer-running and potentially blocking operations.
* @param event the event to publish
* @see #publishEvent(Object)
* @see org.springframework.context.event.ContextRefreshedEvent
* @see org.springframework.context.event.ContextClosedEvent
*/
default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
}
/**
* Notify all <strong>matching</strong> listeners registered with this
* application of an event.
* <p>If the specified {@code event} is not an {@link ApplicationEvent},
* it is wrapped in a {@link PayloadApplicationEvent}.
* <p>Such an event publication step is effectively a hand-off to the
* multicaster and does not imply synchronous/asynchronous execution
* or even immediate execution at all. Event listeners are encouraged
* to be as efficient as possible, individually using asynchronous
* execution for longer-running and potentially blocking operations.
* @param event the event to publish
* @since 4.2
* @see #publishEvent(ApplicationEvent)
* @see PayloadApplicationEvent
*/
void publishEvent(Object event);
}
ApplicationEventPublisher 有一個預設介面方法和介面方法,介面方法需要由具體的子類容器實現。
ApplicationContext
ApplicationContext 這個類就再熟悉不過了,它是 Spring 框架中的核心容器。
如下圖所示,ApplicationContext 介面繼承了 ApplicationEventPublisher 介面,所以常用的 ApplicationContext 就可以用來發布事件。
以上介紹的 Spring 事件監聽釋出角色串起來就是,通過 ApplicationEventPublisher 或者 ApplicationContext 容器釋出 ApplicationEvent 事件並關聯事件物件,然後 ApplicationListener 監聽該事件,當事件釋出後,監聽器就會收執行並獲取到事件及關聯物件。
Spring Boot 觀察者模式實戰
搞懂了 Spring 框架中的事件和監聽機制,那我們還是以上篇中觀察者模式的例子來改造下。
Spring Boot 基礎性的知識和搭建過程就不介紹了,不熟悉的可以關注公眾號Java技術棧,在後臺回覆關鍵字 "boot" 閱讀我之前寫的系列教程。
所有 Spring Boot 教程實戰原始碼在下面個倉庫:
新增觀察者目標類
import lombok.Getter;
import org.springframework.context.ApplicationEvent;
/**
* 觀察目標:棧長
* 來源微信公眾號:Java技術棧
*/
@Getter
public class JavaStackEvent 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 JavaStackEvent(Object source) {
super(source);
}
}
實現 Spring 框架中的 ApplicationEvent 應用程式事件介面,相當於是一個觀察者目標。
新增觀察者類
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
/**
* 觀察者:讀者粉絲
* 來源微信公眾號:Java技術棧
*/
@RequiredArgsConstructor
public class ReaderListener implements ApplicationListener<JavaStackEvent> {
@NonNull
private String name;
private String article;
@Async
@Override
public void onApplicationEvent(JavaStackEvent event) {
// 更新文章
updateArticle(event);
}
private void updateArticle(JavaStackEvent event) {
this.article = (String) event.getSource();
System.out.printf("我是讀者:%s,文章已更新為:%s\n", this.name, this.article);
}
}
實現 Spring 框架中的 ApplicationListener 應用監聽介面,相當於是觀察者。
觀察目標和觀察者類結構圖如下:
新增測試配置類
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Slf4j
@Configuration
public class ObserverConfiguration {
@Bean
public CommandLineRunner commandLineRunner(ApplicationContext context) {
return (args) -> {
log.info("釋出事件:什麼是觀察者模式?");
context.publishEvent(new JavaStackEvent("什麼是觀察者模式?"));
};
}
@Bean
public ReaderListener readerListener1(){
return new ReaderListener("小明");
}
@Bean
public ReaderListener readerListener2(){
return new ReaderListener("小張");
}
@Bean
public ReaderListener readerListener3(){
return new ReaderListener("小愛");
}
}
在 Spring 配置中建立了三個讀者 Bean,在 Spring Boot 啟動後釋出一個觀察者模式事件,然後這三個 Bean 就會收到通知。
輸出結果:
這裡每個讀者建立一個 Bean 可能不太合適,因為要模仿上一個觀察者模式的應用。
實際中的觀察者模式應用應該是指具體業務,舉例說一個電商支付場景,在使用者支付完後可以釋出一個支付事件,然後會有扣減積分,簡訊通知、贈送優惠券等一系列後續的事件監聽器觀察者,這樣可以實現業務解耦,這是一種很典型的應用場景。
如果大家有用到訊息中介軟體,其實也是觀察者模式中釋出訂閱模式的概念。
總結
利用 Spring 中的事件監聽機制也可以輕鬆實現觀察者模式,觀察目標也不需要維護觀察者列表了,相當於釋出-訂閱模式,它們之間是完全解耦的,但每個觀察者需要建立一個 Bean。
好了,今天的分享就到這裡了,又學了一種設計模式的寫法吧,後面棧長我會更新其他設計模式的實戰文章,公眾號Java技術棧第一時間推送。
本節教程所有實戰原始碼已上傳到這個倉庫:
最後,覺得我的文章對你用收穫的話,動動小手,給個在看、轉發,原創不易,棧長需要你的鼓勵。
版權申明:本文系公眾號 "Java技術棧" 原創,原創實屬不易,轉載、引用本文內容請註明出處,禁止抄襲、洗稿,請自重,尊重他人勞動成果和智慧財產權。