最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring事件監(jiān)聽機(jī)制使用和原理示例講解

 更新時(shí)間:2023年06月12日 10:18:16   作者:劉牌  
Spring事件監(jiān)聽機(jī)制是一個(gè)很不錯(cuò)的功能,我們在進(jìn)行業(yè)務(wù)開發(fā)的時(shí)候可以引入,在相關(guān)的開源框架中也是用它的身影,比如高性能網(wǎng)關(guān)ShenYu中就使用了Spring事件監(jiān)聽機(jī)制來發(fā)布網(wǎng)關(guān)的更新數(shù)據(jù),它可以降低系統(tǒng)的耦合性,使系統(tǒng)的擴(kuò)展性更好

前言

好久沒有更新Spring了,今天來分享一下Spring的事件監(jiān)聽機(jī)制,之前分享過一篇Spring監(jiān)聽機(jī)制的使用,今天從原理上進(jìn)行解析,Spring的監(jiān)聽機(jī)制基于觀察者模式,就是就是我們所說的發(fā)布訂閱模式,這種模式可以在一定程度上實(shí)現(xiàn)代碼的解耦,如果想要實(shí)現(xiàn)系統(tǒng)層面的解耦,那么消息隊(duì)列就是我們的不二選擇,消息隊(duì)列本身也是發(fā)布訂閱模式,只是不同的消息隊(duì)列的實(shí)現(xiàn)方式不一樣。

使用

之前的文章我們使用了注解的方式,今天我們使用接口的方式來實(shí)現(xiàn)。

定義事件

如下定義了一個(gè)事件AppEvent,它繼承了ApplicationEvent類,如果我們要使用Spring的事件監(jiān)聽機(jī)制,那么我們定義的事件必須繼承ApplicationEvent ,否則就無法使用。

/**
 * 功能說明: 事件
 * <p>
 * Original @Author: steakliu-劉牌, 2023-03-30  11:02
 * <p>
 * Copyright (C)2020-2022  steakliu All rights reserved.
 */
public class AppEvent extends ApplicationEvent {
    private final String event;
    public AppEvent(Object source, String event) {
        super(source);
        this.event = event;
    }
    public String getEvent() {
        return event;
    }
}

定義事件監(jiān)聽器

事件監(jiān)聽器實(shí)現(xiàn)了ApplicationLister接口,其泛型為ApplicationEvent,因?yàn)橐O(jiān)聽事件,所以必須按照Spring的規(guī)則來,onApplicationEvent方法就是監(jiān)聽到的事件,在這里我們可以進(jìn)行我們的業(yè)務(wù)處理,我們可以看出AppLister我們加上了@Component注解,因?yàn)槭录O(jiān)聽器需要加入Spring IOC容器中才能生效。

/**
 * 功能說明:事件監(jiān)聽器
 * <p>
 * Original @Author: steakliu-劉牌, 2023-03-30  11:03
 * <p>
 * Copyright (C)2020-2022  steakliu All rights reserved.
 */
@Component
public class AppListener implements ApplicationListener<AppEvent> {
    @Override
    public void onApplicationEvent(AppEvent event) {
        System.out.println("event:  "+event.getEvent());
    }
}

事件發(fā)布器

有了事件監(jiān)聽器,就需要發(fā)布事件,所以就需要一個(gè)事件發(fā)布器,事件發(fā)布器使用的是ApplicationEventPublisher,使用它的publishEvent方法進(jìn)行事件發(fā)布。

/**
 * 功能說明:事件發(fā)布器
 * <p>
 * Original @Author: steakliu-劉牌, 2023-06-11  13:55
 * <p>
 * Copyright (C)2020-2022  steakliu All rights reserved.
 */
@Component
public class AppPublisher {
    @Resource
    private ApplicationEventPublisher applicationEventPublisher;
    public void publish(){
        applicationEventPublisher.publishEvent(new AppEvent(new AppListener(),"publish event"));
    }
}

測試

為了方便,這里直接使用SpringBoot來進(jìn)行測試,先獲取AppPublisher,然后調(diào)用publish發(fā)布事件。

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
        AppPublisher publisher = context.getBean(AppPublisher.class);
        publisher.publish();
    }
}

上面整個(gè)事件發(fā)布的代碼就寫完了,我們可以看出其實(shí)還是比較簡單的,里面最核心的三個(gè)組件分別為,事件(Event),監(jiān)聽器(Listener),發(fā)布器(Publisher),實(shí)際使用中我們可以根據(jù)自己的需求去實(shí)現(xiàn)。

原理

上面我們知道了Spring的事件監(jiān)聽機(jī)制的基本使用,那么整個(gè)事件在Spring中是怎么流轉(zhuǎn)的呢,我們很有必要去弄清楚。

我們使用的是SpringBoot項(xiàng)目來進(jìn)行測試,我們先找到SpringBoot對事件監(jiān)聽機(jī)制進(jìn)行處理的入口,然后再進(jìn)行分析,SpringBoot對上下文進(jìn)行處理的入口類是AbstractApplicationContext,它是Spring的入口,其中我們主要關(guān)注的refresh()方法,因?yàn)閞efresh中的方法比較多,我們下面只保留了三個(gè)方法。

@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
    // Initialize event multicaster for this context.
				initApplicationEventMulticaster();
				// Check for listener beans and register them.
				registerListeners();
				// Last step: publish corresponding event.
				finishRefresh();
			}
		}
	}

initApplicationEventMulticaster()

ApplicationEventMulticaster是一個(gè)接口,它定義了如何將ApplicationEvent傳遞給事件監(jiān)聽者(event listener)。該接口有多個(gè)實(shí)現(xiàn)類,可以使用不同的策略將事件分派給不同的監(jiān)聽者。

ApplicationEventMulticaster為Spring事件機(jī)制的核心之一,它支持在應(yīng)用中傳遞事件,并且可以將事件廣播給多個(gè)監(jiān)聽者。在Spring中,事件是由ApplicationEvent及其子類表示的,例如ContextStartedEvent和ContextStoppedEvent等。當(dāng)某些事件發(fā)生時(shí),Spring容器將使用事件廣播機(jī)制來通知感興趣的監(jiān)聽者。

這個(gè)方法的作用是對ApplicationEventMulticaster進(jìn)行賦值,Spring在初始化的時(shí)候會將ApplicationEventMulticaster注冊進(jìn)IOC容器,這里就只是單純從IOC容器中獲取ApplicationEventMulticaster來進(jìn)行賦值,以方便后續(xù)的使用。

protected void initApplicationEventMulticaster() {
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
            this.applicationEventMulticaster =
                    beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
            if (logger.isTraceEnabled()) {
                logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
            }
        } else {
            this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
            beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
            if (logger.isTraceEnabled()) {
                logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
                        "[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
            }
        }
    }

registerListeners()

這個(gè)方法的作用主要就是注冊監(jiān)聽器,它會從IOC容器獲取到我們注冊的監(jiān)聽器,然后將其加入到Multicaster中,在AbstractApplicationEventMulticaster中,使用一個(gè)Set集合來裝監(jiān)聽器。

public final Set<String> applicationListenerBeans = new LinkedHashSet<>();

finishRefresh()

finishRefresh()的作用是發(fā)布事件,里面是一些發(fā)布事件的邏輯,但是由于我們還沒有正式發(fā)布事件,所以這里并不會發(fā)布事件,當(dāng)我們使用applicationEventPublisher的publishEvent方法發(fā)布事件時(shí),才會真正的發(fā)布事件。

ApplicationEventPublisher發(fā)布事件

上面示例中使用ApplicationEventPublisher的publishEvent發(fā)布事件,最終會進(jìn)入AbstractApplicationContext類中進(jìn)行事件發(fā)布,我們只關(guān)注最重要的方法multicastEvent(),它是廣播器ApplicationEventMulticaster的一個(gè)方法事件都是由廣播器進(jìn)行發(fā)布。

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
  getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}

ApplicationEventMulticaster真正發(fā)布事件

ApplicationEventPublisher并沒有真正發(fā)布事件,它相當(dāng)于只是抽象了事件的發(fā)布,為了讓我們更加簡單和方便使用,但是真正發(fā)布事件的是ApplicationEventMulticaster,在multicastEvent()方法中,如果我們配置了線程池,那么事件就會被加入線程池,從而異步執(zhí)行,如果沒有設(shè)置線程池,那么就同步執(zhí)行,最終執(zhí)行都是調(diào)用invokeListener()方法。

public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        Executor executor = getTaskExecutor();
        for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            if (executor != null) {
                executor.execute(() -> invokeListener(listener, event));
            } else {
                invokeListener(listener, event);
            }
        }
    }

默認(rèn)是不會使用線程池的,如果我們需要事件異步執(zhí)行,那么可以配置線程池,其核心就是給廣播器SimpleApplicationEventMulticaster的成員變量taskExecutor設(shè)置

/**
 * 功能說明: 事件任務(wù)線程池
 * <p>
 * Original @Author: steakliu-劉牌, 2023-06-11  13:17
 * <p>
 * Copyright (C)2020-2022  steakliu All rights reserved.
 */
@Configuration
public class TaskExecutor {
    @Bean("eventTaskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setCorePoolSize(10);
        threadPoolTaskExecutor.setMaxPoolSize(20);
        threadPoolTaskExecutor.setKeepAliveSeconds(10);
        threadPoolTaskExecutor.setThreadNamePrefix("application-event-thread");
        threadPoolTaskExecutor.setQueueCapacity(100);
        threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true);
        threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true);
        threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());
        threadPoolTaskExecutor.initialize();
        return threadPoolTaskExecutor;
    }
    @Bean
    public ApplicationEventMulticaster applicationEventMulticaster() {
        SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
        simpleApplicationEventMulticaster.setTaskExecutor(taskExecutor());
        return simpleApplicationEventMulticaster;
    }
}

invokeListener

invokeListener最終會通過傳入的監(jiān)聽器去調(diào)用目標(biāo)監(jiān)聽器,也就是我們自定義的監(jiān)聽器,主要代碼如下,我們可以看到最終調(diào)用onApplicationEvent方法,就是我們上面示例AppListener監(jiān)聽器的onApplicationEvent方法。

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
		 listener.onApplicationEvent(event);
}    

到這里,整個(gè)流程就完了,我們梳理一下重要的組件。

  • ApplicationEvent
  • ApplicationListener
  • ApplicationEventPublisher
  • ApplicationEventMulticaster

上面的四個(gè)組件基本上就是Spring事件監(jiān)聽機(jī)制的全部,ApplicationEvent是事件的規(guī)范,ApplicationListener是監(jiān)聽器,ApplicationEventPublisher是發(fā)布器,ApplicationEventMulticaster是廣播器,其實(shí)ApplicationEventMulticaster和ApplicationEventPublisher本質(zhì)是一樣的,都能完成事件的發(fā)布,ApplicationEventPublisher最終也是去調(diào)用ApplicationEventMulticaster,只不過它只專注于事件發(fā)布,單獨(dú)提出一個(gè)接口來,職責(zé)更加單一,這也是一種設(shè)計(jì)思想。

總結(jié)

上面對Spring事件監(jiān)聽機(jī)制的使用和原理進(jìn)行了詳細(xì)的介紹,并對其中涉及的組件進(jìn)行解析,Spring事件監(jiān)聽機(jī)制是一個(gè)很不錯(cuò)的功能,我們在進(jìn)行業(yè)務(wù)開發(fā)的時(shí)候可以引入,在相關(guān)的開源框架中也是用它的身影,比如高性能網(wǎng)關(guān)ShenYu中就使用了Spring事件監(jiān)聽機(jī)制來發(fā)布網(wǎng)關(guān)的更新數(shù)據(jù),它可以降低系統(tǒng)的耦合性,使系統(tǒng)的擴(kuò)展性更好。

到此這篇關(guān)于Spring事件監(jiān)聽機(jī)制使用和原理解析的文章就介紹到這了,更多相關(guān)Spring事件監(jiān)聽內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 利用java模擬實(shí)現(xiàn)鍵盤鼠標(biāo)操作(附源碼)

    利用java模擬實(shí)現(xiàn)鍵盤鼠標(biāo)操作(附源碼)

    這篇文章主要為大家詳細(xì)介紹了如何從零設(shè)計(jì)并實(shí)現(xiàn)一個(gè)功能完備的鍵盤鼠標(biāo)模擬庫,提供比原生?Robot?更友好的?API,更高的可定制性和可擴(kuò)展性,感興趣的小伙伴可以了解一下
    2025-05-05
  • Java中數(shù)組array和列表list相互轉(zhuǎn)換

    Java中數(shù)組array和列表list相互轉(zhuǎn)換

    這篇文章主要介紹了Java中數(shù)組array和列表list相互轉(zhuǎn)換,在Java中,可以將數(shù)組(array)和列表(list)相互轉(zhuǎn)換,但需要注意一些細(xì)節(jié)和限制,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-09-09
  • java實(shí)現(xiàn)圖片轉(zhuǎn)ascii字符畫的方法示例

    java實(shí)現(xiàn)圖片轉(zhuǎn)ascii字符畫的方法示例

    這篇文章主要介紹了java實(shí)現(xiàn)圖片轉(zhuǎn)ascii字符畫的方法示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-08-08
  • 在SpringBoot中,如何使用Netty實(shí)現(xiàn)遠(yuǎn)程調(diào)用方法總結(jié)

    在SpringBoot中,如何使用Netty實(shí)現(xiàn)遠(yuǎn)程調(diào)用方法總結(jié)

    我們在進(jìn)行網(wǎng)絡(luò)連接的時(shí)候,建立套接字連接是一個(gè)非常消耗性能的事情,特別是在分布式的情況下,用線程池去保持多個(gè)客戶端連接,是一種非常消耗線程的行為.那么我們該通過什么技術(shù)去解決上述的問題呢,那么就不得不提一個(gè)網(wǎng)絡(luò)連接的利器——Netty,需要的朋友可以參考下
    2021-06-06
  • Java實(shí)現(xiàn)藍(lán)橋杯數(shù)獨(dú)游戲的示例代碼

    Java實(shí)現(xiàn)藍(lán)橋杯數(shù)獨(dú)游戲的示例代碼

    這篇文章主要介紹了Java實(shí)現(xiàn)藍(lán)橋杯數(shù)獨(dú)游戲的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • java管道piped輸入流與輸出流應(yīng)用場景案例分析

    java管道piped輸入流與輸出流應(yīng)用場景案例分析

    這篇文章主要介紹了java管道流PipedInputStream與PipedOutputStream(輸入流與輸出流)的應(yīng)用場景案例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2022-02-02
  • 淺析Java中線程的創(chuàng)建和啟動

    淺析Java中線程的創(chuàng)建和啟動

    這篇文章運(yùn)用實(shí)例代碼介紹了Java中線程的創(chuàng)建和啟動,非常詳細(xì),有需要的朋友們可以參考借鑒,下面一起來看看。
    2016-08-08
  • idea如何debug看springsecurity的過濾器順序

    idea如何debug看springsecurity的過濾器順序

    這篇文章主要介紹了idea如何debug看springsecurity的過濾器順序,文中通過圖文結(jié)合的方式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2024-04-04
  • 使用@Value注解從配置文件中讀取數(shù)組

    使用@Value注解從配置文件中讀取數(shù)組

    這篇文章主要介紹了使用@Value注解從配置文件中讀取數(shù)組的操作,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • java按指定編碼寫入和讀取文件內(nèi)容的類分享

    java按指定編碼寫入和讀取文件內(nèi)容的類分享

    這篇文章主要介紹了java按指定編碼寫入和讀取文件內(nèi)容的類,需要的朋友可以參考下
    2014-02-02

最新評論

屏南县| 朝阳区| 南川市| 泗洪县| 蛟河市| 阿拉善右旗| 莱阳市| 栾城县| 沐川县| 视频| 揭西县| 西安市| 东明县| 大厂| 临沭县| 游戏| 思茅市| 遂宁市| 松原市| 富裕县| 泗洪县| 车险| 鄂州市| 南郑县| 平利县| 黎川县| 贵溪市| 泾阳县| 阿城市| 嘉定区| 曲麻莱县| 合水县| 柘城县| 常熟市| 靖西县| 黄山市| 临清市| 梨树县| 天长市| 宣城市| 昌乐县|