Spring Boot(三):Spring Boot中的事件的使用 與Spring Boot啟動流程(Event 事件 和 Listeners監聽器)

C_LEE發表於2021-07-20
前言:在講述內容之前 希望大家對設計模式有所瞭解 即使你學會了本片的內容 也不知道什麼時候去使用 或者為什麼要這樣去用

觀察者模式:

觀察者模式是一種物件行為模式。它定義物件間的一種一對多的依賴關係,當一個物件的狀態發生改變時,所有依賴於它的物件都得到通知並被自動更新。在觀察者模式中,主體是通知的釋出者,它發出通知時並不需要知道誰是它的觀察者,可以有任意數目的觀察者訂閱並接收通知。觀察者模式不僅被廣泛應用於軟體介面元素之間的互動,在業務物件之間的互動、許可權管理等方面也有廣泛的應用


觀察者模式(Observer)完美的將觀察者和被觀察的物件分離開。舉個例子,使用者介面可以作為一個觀察者,業務資料是被觀察者,使用者介面觀察業務資料的變化,發現資料變化後,就顯示在介面上。物件導向設計的一個原則是:系統中的每個類將重點放在某一個功能上,而不是其他方面。一個物件只做一件事情,並且將他做好。觀察者模式在模組之間劃定了清晰的界限,提高了應用程式的可維護性和重用性。


觀察者設計模式定義了物件間的一種一對多的組合關係,以便一個物件的狀態發生變化時,所有依賴於它的物件都得到通知並自動重新整理。(以上源於百度)

已經熟悉設計模式的可以直接向下閱讀 對不熟悉的 希望通過上述內容 你們可以有一定的瞭解 通俗點說 觀察者模式 是設計框架的一種
當然我們的Spring Boot當然也不會放過這麼好的設計模式 那麼Spring Boot中又有哪些地方使用到了

1.ApplicationStartingEvent

當應用啟動還沒有進行任何處理時,在對所有的監聽器做初始化的時候傳送的事件
public ConfigurableApplicationContext run(String... args) {
	//記錄服務啟動事件
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
	//開啟awt的headless模式
	//Headless模式是系統的一種配置模式。在系統可能缺少顯示裝置、鍵盤或滑鼠這些外設的情況下可以使用該模式
	this.configureHeadlessProperty();
	//獲取監聽器列表
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
	啟動所有監聽器
        listeners.starting();
.................
    }

此時監聽列表中只有一個監聽事件為EventPublishingRunListener

Spring Boot(三):Spring Boot中的事件的使用 與Spring Boot啟動流程(Event 事件 和 Listeners監聽器)

它的作用就是通知Spring Boot專案開始啟動

Spring Boot(三):Spring Boot中的事件的使用 與Spring Boot啟動流程(Event 事件 和 Listeners監聽器)

2.ApplicationEnvironmentPreparedEvent

當已獲取的了所有Spring Context上下文資訊 但是此時還沒有進行建立

此時Spring Boot開始啟動 EventPublishingRunListener會傳送ApplicationEnvironmentPreparedEvent事件 告訴Spring Boot應用環境已經準備就緒 準備做後續處理 監聽此事件的監聽器是ConfigFileApplicationListener

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
	//獲取環境 資源 載入器
        List<EnvironmentPostProcessor> postProcessors = this.loadPostProcessors();
        postProcessors.add(this);
	//對Order的大小進行排序 裝載
	AnnotationAwareOrderComparator.sort(postProcessors);
	//使用迭代器 根據順序開始執行 環境 資源 的配置
        Iterator var3 = postProcessors.iterator();

        while(var3.hasNext()) {
            EnvironmentPostProcessor postProcessor = (EnvironmentPostProcessor)var3.next();
            postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
        }

    }

3.ApplicationContextInitializedEvent

測試Spring Context上下文已經初始化完畢 但是此時上下文是的空的

開始向Spring上下文裝填內容

public void contextPrepared(ConfigurableApplicationContext context) {
	//獲取當前所有有關的上下文的監聽器
        Iterator var2 = this.listeners.iterator();

        while(var2.hasNext()) {
            SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next();
	//通知
	listener.contextPrepared(context);
        }

    }
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
	//向上下文中裝填配置引數    
	//context.setEnvironment(environment);
	//嚮應用中裝填上下文
        this.postProcessApplicationContext(context);
	//進行初始化引數裝填        
	this.applyInitializers(context);
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            this.logStartupInfo(context.getParent() == null);
            this.logStartupProfileInfo(context);
        }

        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }

        if (beanFactory instanceof DefaultListableBeanFactory) {
            ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }

        Set<Object> sources = this.getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        this.load(context, sources.toArray(new Object[0]));
        listeners.contextLoaded(context);
    }

4.ApplicationPreparedEvent

應用載入完畢
public void contextLoaded(ConfigurableApplicationContext context) {
        ApplicationListener listener;
	//相容器中裝填已經初始化的上下文物件
        for(Iterator var2 = this.application.getListeners().iterator(); var2.hasNext(); context.addApplicationListener(listener)) {
            listener = (ApplicationListener)var2.next();
            if (listener instanceof ApplicationContextAware) {
                ((ApplicationContextAware)listener).setApplicationContext(context);
            }
        }
	//通知
        this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
    }

5.ApplicationStartedEvent

應用初始化完成
public void started(ConfigurableApplicationContext context) {
	//通知
	context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
}
try {
     listeners.running(context);
     return context;
} catch (Throwable var9) {
     this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
     throw new IllegalStateException(var9);
}

6.ApplicationReadyEvent

應用載入完畢
public void running(ConfigurableApplicationContext context) {
    context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
}

這就是Spring在啟動的時候 發生的事件互動 最直觀的我們可以看出 當一個具體的功能實現完畢之後 建立事件 去通知下一個階段去做相對的事情 這樣子比在一個方法裡面 去直接呼叫好得多 並且可以支援廣播模式(一對多)
當然在Spring Boot中使用觀察者模式 也非常簡單 首先我們建立一個名為旅遊的事件

@Data
public class TravelEvent {

    private String money;

    private String location;

    private String sex;

}

當我們需要去旅行時 需要帶錢 帶男/女朋友 去什麼地方遊玩
註冊一個監聽類用來監聽我們發出的事件

1.監聽類的例項必須由Spring容器管理 切必須有例項 否則無法監聽到事件的發生
2.在方法上使用@EventListener(事件類)註解 可以將事件例項當做入參 進行業務處理
3.使用ApplicationEventPublisher傳送事件 進行測試
@Component
public class TravelEventListener {

    @EventListener(TravelEvent.class)
    public void location(TravelEvent travelEvent){
        System.out.println("地方:"+travelEvent.getLocation());
    }

    @EventListener(TravelEvent.class)
    public void sex(TravelEvent travelEvent){
        System.out.println("朋友:"+travelEvent.getSex());
    }

    @EventListener(TravelEvent.class)
    public void money(TravelEvent travelEvent){
        System.out.println("錢:"+travelEvent.getMoney());
    }
}
@RestController
@RequestMapping("/event")
public class EventController {

    @Autowired
    ApplicationEventPublisher applicationEventPublisher;

    @PostMapping("/travel")
    public String goTravel(@RequestBody TravelEvent travelEvent){
        applicationEventPublisher.publishEvent(travelEvent);
        return "ok";
    }

}
Spring Boot(三):Spring Boot中的事件的使用 與Spring Boot啟動流程(Event 事件 和 Listeners監聽器)
Spring Boot(三):Spring Boot中的事件的使用 與Spring Boot啟動流程(Event 事件 和 Listeners監聽器)

最後 Spring Boot中啟動時 所發生的事件 已經在Spring Boot中如何使用事件已經講完了 可以看出在Spring Boot中使用觀察者模式 還是非常方便的 作為一名程式設計師 尤其是後臺人員務必利用好設計模式 如果一個專案 沒有使用任何設計模式 那麼還不如去寫程式導向 一旦有需求的變更 我們就可能成為 外行眼裡吐槽的 加班狗 地中海等等......

相關文章