Spring監聽器---ApplicationListener


說到事件監聽,想到的肯定是觀察者模式。但是這兒主要說下spring中的監聽是怎么樣的流程。

這就不得不說到spring容器的refresh方法,容器啟動過程中,listener相關的主要是這個三個方法:initApplicationEventMulticaster方法初始化事件多播器,后續的事件發布都是由多播器來發布的;registerListeners注冊監聽器到前面初始化好的多播器上面去;

finishRefresh容器啟動完成最后刷新,發布ContextRefreshedEvent事件。

 

 

1.初始化多播器:獲取bean工廠對象ConfigurableListableBeanFactory,判斷容器中是否有applicationEventMulticaster多播器,如果沒有則創建一個一個簡單事件多播器SimpleApplicationEventMulticaster並注冊到容器中,后續使用

 

 

 

2.注冊監聽器到多播器上並發布早期事件:首先獲取容器中已有的監聽器(成品對象,從第一張圖中可以看到我們自己的組件對象在registerListeners方法調用的時候 還沒有初始化,是在下面的finishBeanFactoryInitialization方法中才進行初始化的),注冊到多播器;然后獲取bean定義中的監聽器,也就是我們自己定義的監聽器;同樣也注冊到多播器上去;最后如果有早期事件就去發布早期事件(multicastEvent方法),這些事件只能由已經實例化的監聽器監聽,我們自己的監聽器初始化是在finishBeanFactoryInitialization方法中。

 

 

發布事件:multicastEvent方法----->invokeListener方法---->doInvokeListener方法調用監聽器的onApplicationEvent

可以看到這里支持異步發送,但是從上面我們初始化簡單多播器的時候,executer對象並沒有賦值,因此始終是同步發布。如果我們想實現異步發布事件,那么就要讓上面初始化多播器的邏輯走進第一個分支。我們可以在容器中自己繼承SimpleApplicationEventMulticaster,並初始化一個線程池,然后將其注冊到容器中,bean的名字必須使用“applicationEventMulticaster”,因為此時容器還沒有創建真正的對象,只有這個名字的bean定義才會馬上去創建對象。這樣就可以實現異步發布事件了。

 

3.執行finishRefresh方法發布ContextRefreshedEvent事件,標志的容器已經啟動完成。

 

 

 

監聽器的流程完了,我們現在來看下使用

首先實現一個自己的監聽器

package com.nijunyang.spring.listener;

        import org.springframework.context.ApplicationEvent;
        import org.springframework.context.ApplicationListener;
        import org.springframework.stereotype.Component;

/**
 * Description:
 * Created by nijunyang on 2020/2/20 21:53
 */
@Component
public class MyListener implements ApplicationListener<ApplicationEvent> {
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println("收到事件:" + event.toString());
    }
}

 

新建一個自己的事件:

package com.nijunyang.spring.listener;

import org.springframework.context.ApplicationEvent;

/**
 * Description:
 * Created by nijunyang on 2020/2/20 22:05
 */
public class MyApplicationEvent extends ApplicationEvent {
    /**
     * Create a new ApplicationEvent.
     *
     * @param source the object on which the event initially occurred (never {@code null})
     */
    public MyApplicationEvent(Object source) {
        super(source);
    }
}

配置類:指定要掃描的包

package com.nijunyang.spring;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * @author: create by nijunyang
 * @date:2019/10/6
 */
@Configuration
@ComponentScan(basePackages = "com.nijunyang.spring.*")
public class MainConfig {
}

測試代碼:在容器創建完之后發布自己的事件。

package com.nijunyang.spring;

import com.nijunyang.spring.listener.MyApplicationEvent;
import com.nijunyang.spring.model.Student;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Application {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
        context.publishEvent(new MyApplicationEvent("想漲工資"));
    }

}

執行代碼會發現我們的監聽器會監聽到兩個事件,因為我們監聽器監聽的事件是ApplicationEvent,上面說到容器啟動的時候,最后會執行finishRefresh方法發布ContextRefreshedEvent事件,容器啟動完成之后,我們自己手動發布了一個我們自己的事件,因此會監聽到兩個事件。

 

 

 修改我們的監聽器,只監聽我們自己的事件:

package com.nijunyang.spring.listener;

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

/**
 * Description:
 * Created by nijunyang on 2020/2/20 21:53
 */
@Component
public class MyListener implements ApplicationListener<MyApplicationEvent> {
    @Override
    public void onApplicationEvent(MyApplicationEvent event) {
        System.out.println("收到事件:" + event.toString());
    }
}

 

 再次執行代碼,發現現在就只能監聽我們自己的事件了

 

 

通過spring的監聽器,我們不僅可以實現自己相關的業務,還可以通過這個機制將我們自己的組件和spring進行整合,比如阿里的nacos就是通過ApplicationListener與spring整合的;

springboot和spring中的一些事件:

ContextClosedEvent:容器關閉的時候,我們可以監聽這個事件在容器關閉的時候去清理一些緩存(比如redis)的數據

 

ApplicationFailedEvent:該事件為spring boot啟動失敗時的操作

ApplicationPreparedEvent:上下文context准備時觸發

ApplicationReadyEvent:上下文已經准備完畢的時候觸發,做權限認證的時候。在這個時候就可以去初始化一些權限數據。或者預備其他數據

ApplicationEnvironmentPreparedEvent:環境事先准備

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM