国产成人精品亚洲777人妖,欧美日韩精品一区视频,最新亚洲国产,国产乱码精品一区二区亚洲

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

詳解Spring事件發(fā)布與監(jiān)聽(tīng)機(jī)制

瀏覽:41日期:2023-07-05 14:44:49
目錄一、ApplicationContext 事件機(jī)制二、ApplicationListener 監(jiān)聽(tīng)器三、ApplicationEvent 事件四、自定義事件和監(jiān)聽(tīng)器五、注解式監(jiān)聽(tīng)器一、ApplicationContext 事件機(jī)制

ApplicationContext 事件機(jī)制采用觀察者設(shè)計(jì)模式來(lái)實(shí)現(xiàn),通過(guò) ApplicationEvent 事件類和 ApplicationListener 監(jiān)聽(tīng)器接口,可以實(shí)現(xiàn) ApplicationContext 事件發(fā)布與處理。

每當(dāng) ApplicationContext 發(fā)布 ApplicationEvent 時(shí),如果 Spring 容器中有 ApplicationListener bean,則監(jiān)聽(tīng)器會(huì)被觸發(fā)執(zhí)行相應(yīng)的處理。當(dāng)然,ApplicationEvent 事件的發(fā)布需要顯示觸發(fā),要么 Spring 顯示觸發(fā),要么我們顯示觸發(fā)。

二、ApplicationListener 監(jiān)聽(tīng)器

定義應(yīng)用監(jiān)聽(tīng)器需要實(shí)現(xiàn)的接口。此接口繼承了 JDK 標(biāo)準(zhǔn)的事件監(jiān)聽(tīng)器接口 EventListener,EventListener 接口是一個(gè)空的標(biāo)記接口,推薦所有事件監(jiān)聽(tīng)器必須要繼承它。

package org.springframework.context;import java.util.EventListener;@FunctionalInterfacepublic interface ApplicationListener<E extends ApplicationEvent> extends EventListener {/** * 處理應(yīng)用事件 */void onApplicationEvent(E event);}

package java.util;public interface EventListener {}

ApplicationListener 是個(gè)泛型接口,我們自定義此接口的實(shí)現(xiàn)類時(shí),如果指定了泛型的具體事件類,那么只會(huì)監(jiān)聽(tīng)此事件。如果不指定具體的泛型,則會(huì)監(jiān)聽(tīng) ApplicationEvent 抽象類的所有子類事件。

如下我們定義一個(gè)監(jiān)聽(tīng)器,監(jiān)聽(tīng)具體的事件,例如監(jiān)聽(tīng) ApplicationStartedEvent 事件。

package com.chenpi;import lombok.extern.slf4j.Slf4j;import org.springframework.boot.context.event.ApplicationStartedEvent;import org.springframework.context.ApplicationListener;import org.springframework.stereotype.Component;@Slf4j@Componentpublic class MyApplicationListener implements ApplicationListener<ApplicationStartedEvent> { @Override public void onApplicationEvent(ApplicationStartedEvent event) {log.info('>>> MyApplicationListener:{}', event); }}

啟動(dòng)服務(wù),會(huì)發(fā)現(xiàn)在服務(wù)啟動(dòng)后,此監(jiān)聽(tīng)器被觸發(fā)了。

詳解Spring事件發(fā)布與監(jiān)聽(tīng)機(jī)制

如果不指定具體的泛型類,則會(huì)監(jiān)聽(tīng) ApplicationEvent 抽象類的所有子類事件。如下所示:

package com.chenpi;import lombok.extern.slf4j.Slf4j;import org.springframework.context.ApplicationEvent;import org.springframework.context.ApplicationListener;import org.springframework.stereotype.Component;@Slf4j@Componentpublic class MyApplicationListener implements ApplicationListener { @Override public void onApplicationEvent(ApplicationEvent event) {log.info('>>> MyApplicationListener:{}', event); }}

詳解Spring事件發(fā)布與監(jiān)聽(tīng)機(jī)制

注意,監(jiān)聽(tīng)器類的 bean 要注入到 Spring 容器中,不然不會(huì)生效。一種是使用注解注入,例如 @Component。另外可以使用 SpringApplicationBuilder.listeners() 方法添加,不過(guò)這兩種方式有區(qū)別的,看以下示例。

首先我們使用 @Component 注解方式,服務(wù)啟動(dòng)時(shí),監(jiān)視到了2個(gè)事件:

ApplicationStartedEvent ApplicationReadyEvent

package com.chenpi;import lombok.extern.slf4j.Slf4j;import org.springframework.boot.context.event.SpringApplicationEvent;import org.springframework.context.ApplicationListener;import org.springframework.stereotype.Component;@Slf4j@Componentpublic class MyApplicationListener implements ApplicationListener<SpringApplicationEvent> { @Override public void onApplicationEvent(SpringApplicationEvent event) {log.info('>>> MyApplicationListener:{}', event); }}

詳解Spring事件發(fā)布與監(jiān)聽(tīng)機(jī)制

而使用 SpringApplicationBuilder.listeners() 方法添加監(jiān)聽(tīng)器,服務(wù)啟動(dòng)時(shí),監(jiān)聽(tīng)到了5個(gè)事件:

ApplicationEnvironmentPreparedEvent ApplicationContextInitializedEvent ApplicationPreparedEvent ApplicationStartedEvent ApplicationReadyEvent

package com.chenpi;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.boot.builder.SpringApplicationBuilder;import org.springframework.context.ConfigurableApplicationContext;@SpringBootApplicationpublic class Application { public static void main(String[] args) {// SpringApplication.run(Application.class, args);SpringApplication app = new SpringApplicationBuilder(Application.class).listeners(new MyApplicationListener()).build();app.run(args); }}

詳解Spring事件發(fā)布與監(jiān)聽(tīng)機(jī)制

其實(shí)這是和監(jiān)聽(tīng)器 bean 注冊(cè)的時(shí)機(jī)有關(guān),@component 注解的監(jiān)聽(tīng)器 bean 只有在 bean 初始化注冊(cè)完后才能使用;而通過(guò) SpringApplicationBuilder.listeners() 添加的監(jiān)聽(tīng)器 bean 是在容器啟動(dòng)前,所以監(jiān)聽(tīng)到的事件比較多。但是注意,這兩個(gè)不要同時(shí)使用,不然監(jiān)聽(tīng)器會(huì)重復(fù)執(zhí)行兩遍。

如果你想在監(jiān)聽(tīng)器 bean 中注入其他 bean(例如 @Autowired),那最好是使用注解形式,因?yàn)槿绻绨l(fā)布監(jiān)聽(tīng)器,可能其他 bean 還未初始化完成,可能會(huì)報(bào)錯(cuò)。

三、ApplicationEvent 事件

ApplicationEvent 是所有應(yīng)用事件需要繼承的抽象類。它繼承了 EventObject 類,EventObject 是所有事件的根類,這個(gè)類有個(gè) Object 類型的對(duì)象 source,代表事件源。所有繼承它的類的構(gòu)造函數(shù)都必須要顯示傳遞這個(gè)事件源。

package org.springframework.context;import java.util.EventObject;public abstract class ApplicationEvent extends EventObject {private static final long serialVersionUID = 7099057708183571937L;// 發(fā)布事件的系統(tǒng)時(shí)間private final long timestamp;public ApplicationEvent(Object source) {super(source);this.timestamp = System.currentTimeMillis();}public final long getTimestamp() {return this.timestamp;}}

package java.util;public class EventObject implements java.io.Serializable { private static final long serialVersionUID = 5516075349620653480L; protected transient Object source; public EventObject(Object source) {if (source == null) throw new IllegalArgumentException('null source');this.source = source; } public Object getSource() {return source; } public String toString() {return getClass().getName() + '[source=' + source + ']'; }}

在 Spring 中,比較重要的事件類是 SpringApplicationEvent。Spring 有一些內(nèi)置的事件,當(dāng)完成某種操作時(shí)會(huì)觸發(fā)某些事件。這些內(nèi)置事件繼承 SpringApplicationEvent 抽象類。SpringApplicationEvent 繼承 ApplicationEvent 并增加了字符串?dāng)?shù)組參數(shù)字段 args。

@SuppressWarnings('serial')public abstract class SpringApplicationEvent extends ApplicationEvent {private final String[] args;public SpringApplicationEvent(SpringApplication application, String[] args) {super(application);this.args = args;}public SpringApplication getSpringApplication() {return (SpringApplication) getSource();}public final String[] getArgs() {return this.args;}}

詳解Spring事件發(fā)布與監(jiān)聽(tīng)機(jī)制

我們可以編寫(xiě)自己的監(jiān)聽(tīng)器,然后監(jiān)聽(tīng)這些事件,實(shí)現(xiàn)自己的業(yè)務(wù)邏輯。例如編寫(xiě) ApplicationListener 接口的實(shí)現(xiàn)類,監(jiān)聽(tīng) ContextStartedEvent 事件,當(dāng)應(yīng)用容器 ApplicationContext 啟動(dòng)時(shí),會(huì)發(fā)布該事件,所以我們編寫(xiě)的監(jiān)聽(tīng)器會(huì)被觸發(fā)。

ContextRefreshedEvent:ApplicationContext 被初始化或刷新時(shí),事件被發(fā)布。ConfigurableApplicationContext接口中的 refresh() 方法被調(diào)用也會(huì)觸發(fā)事件發(fā)布。初始化是指所有的 Bean 被成功裝載,后處理 Bean 被檢測(cè)并激活,所有單例 Bean 被預(yù)實(shí)例化,ApplicationContext 容器已就緒可用。 ContextStartedEvent:應(yīng)用程序上下文被刷新后,但在任何 ApplicationRunner 和 CommandLineRunner 被調(diào)用之前,發(fā)布此事件。 ApplicationReadyEvent:此事件會(huì)盡可能晚地被發(fā)布,以表明應(yīng)用程序已準(zhǔn)備好為請(qǐng)求提供服務(wù)。事件源是SpringApplication 本身,但是要注意修改它的內(nèi)部狀態(tài),因?yàn)榈侥菚r(shí)所有初始化步驟都已經(jīng)完成了。 ContextStoppedEvent:ConfigurableApplicationContext 接口的 stop() 被調(diào)用停止 ApplicationContext 時(shí),事件被發(fā)布。 ContextClosedEvent:ConfigurableApplicationContext 接口的 close() 被調(diào)用關(guān)閉 ApplicationContext 時(shí),事件被發(fā)布。注意,一個(gè)已關(guān)閉的上下文到達(dá)生命周期末端后,它不能被刷新或重啟。 ApplicationFailedEvent:當(dāng)應(yīng)用啟動(dòng)失敗后發(fā)布事件。 ApplicationEnvironmentPreparedEvent:事件是在 SpringApplication 啟動(dòng)時(shí)發(fā)布的,并且首次檢查和修改 Environment 時(shí),此時(shí)上 ApplicationContext 還沒(méi)有創(chuàng)建。 ApplicationPreparedEvent:事件發(fā)布時(shí),SpringApplication 正在啟動(dòng),ApplicationContext 已經(jīng)完全準(zhǔn)備好,但沒(méi)有刷新。在這個(gè)階段,將加載 bean definitions 并準(zhǔn)備使用 Environment。 RequestHandledEvent:這是一個(gè) web 事件,只能應(yīng)用于使用 DispatcherServlet 的 Web 應(yīng)用。在使用 Spring 作為前端的 MVC 控制器時(shí),當(dāng) Spring 處理用戶請(qǐng)求結(jié)束后,系統(tǒng)會(huì)自動(dòng)觸發(fā)該事件。四、自定義事件和監(jiān)聽(tīng)器

前面介紹了自定義監(jiān)聽(tīng)器,然后監(jiān)聽(tīng) Spring 原有的事件。下面介紹自定義事件和自定義監(jiān)聽(tīng)器,然后在程序中發(fā)布事件,觸發(fā)監(jiān)聽(tīng)器執(zhí)行,實(shí)現(xiàn)自己的業(yè)務(wù)邏輯。

首先自定義事件,繼承 ApplicationEvent,當(dāng)然事件可以自定義自己的屬性。

package com.chenpi;import lombok.Getter;import lombok.Setter;import lombok.ToString;import org.springframework.context.ApplicationEvent;@Getter@Setterpublic class MyApplicationEvent extends ApplicationEvent { // 事件可以增加自己的屬性 private String myField; public MyApplicationEvent(Object source, String myField) {// 綁定事件源super(source);this.myField = myField; } @Override public String toString() {return 'MyApplicationEvent{' + 'myField=’' + myField + ’’’ + ', source=' + source + ’}’; }}

然后自定義監(jiān)聽(tīng)器,監(jiān)聽(tīng)我們自定義的事件。

package com.chenpi;import lombok.extern.slf4j.Slf4j;import org.springframework.context.ApplicationListener;@Slf4jpublic class MyApplicationListener implements ApplicationListener<MyApplicationEvent> { @Override public void onApplicationEvent(MyApplicationEvent event) {log.info('>>> MyApplicationListener:{}', event); }}

注冊(cè)監(jiān)聽(tīng)器和發(fā)布事件。注冊(cè)監(jiān)聽(tīng)器上面講解了有兩種方式。事件的發(fā)布可以通過(guò) ApplicationEventPublisher.publishEvent() 方法。此處演示直接用 configurableApplicationContext 發(fā)布,它實(shí)現(xiàn)了 ApplicationEventPublisher 接口。

package com.chenpi;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.boot.builder.SpringApplicationBuilder;import org.springframework.context.ConfigurableApplicationContext;@SpringBootApplicationpublic class Application { public static void main(String[] args) {// SpringApplication.run(Application.class, args);// 注冊(cè)監(jiān)聽(tīng)器SpringApplication app = new SpringApplicationBuilder(Application.class).listeners(new MyApplicationListener()).build();ConfigurableApplicationContext configurableApplicationContext = app.run(args);// 方便演示,在項(xiàng)目啟動(dòng)后發(fā)布事件,當(dāng)然也可以在其他操作和其他時(shí)間點(diǎn)發(fā)布事件configurableApplicationContext.publishEvent(new MyApplicationEvent('我是事件源,項(xiàng)目啟動(dòng)成功后發(fā)布事件', '我是自定義事件屬性')); }}

啟動(dòng)服務(wù),結(jié)果顯示確實(shí)監(jiān)聽(tīng)到發(fā)布的事件了。

2021-06-26 16:15:09.584  INFO 10992 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8081 (http) with context path ’’

2021-06-26 16:15:09.601  INFO 10992 --- [           main] com.chenpi.Application                   : Started Application in 2.563 seconds (JVM running for 4.012)

2021-06-26 16:15:09.606  INFO 10992 --- [           main] com.chenpi.MyApplicationListener         : >>> MyApplicationListener:MyApplicationEvent{myField=’我是自定義事件屬性’, source=我是事件源,項(xiàng)目啟動(dòng)成功后發(fā)布事件}

事件監(jiān)聽(tīng)機(jī)制能達(dá)到分發(fā),解耦效果。例如可以在業(yè)務(wù)類中發(fā)布事件,讓監(jiān)聽(tīng)在此事件的監(jiān)聽(tīng)器執(zhí)行自己的業(yè)務(wù)處理。例如:

package com.chenpi;import org.springframework.context.ApplicationEventPublisher;import org.springframework.context.ApplicationEventPublisherAware;import org.springframework.stereotype.Service;@Servicepublic class MyService implements ApplicationEventPublisherAware { private ApplicationEventPublisher applicationEventPublisher; @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.applicationEventPublisher = applicationEventPublisher; } public void testEvent() {applicationEventPublisher.publishEvent(new MyApplicationEvent('我是事件源', '我是自定義事件屬性')); }}五、注解式監(jiān)聽(tīng)器

除了實(shí)現(xiàn) ApplicationListener 接口創(chuàng)建監(jiān)聽(tīng)器外,Spring 還提供了注解 @EventListener 來(lái)創(chuàng)建監(jiān)聽(tīng)器。

package com.chenpi;import org.springframework.context.event.EventListener;import org.springframework.stereotype.Component;import lombok.extern.slf4j.Slf4j;@Slf4j@Componentpublic class MyApplicationListener01 { @EventListener public void onApplicationEvent(MyApplicationEvent event) {log.info('>>> MyApplicationListener:{}', event); }}

而且注解還可以通過(guò)條件過(guò)濾只監(jiān)聽(tīng)指定條件的事件。例如事件的 myField 屬性的值等于'陳皮'的事件。

package com.chenpi;import org.springframework.context.event.EventListener;import org.springframework.stereotype.Component;import lombok.extern.slf4j.Slf4j;@Slf4j@Componentpublic class MyApplicationListener01 { @EventListener(condition = '#event.myField.equals(’陳皮’)') public void onApplicationEvent(MyApplicationEvent event) {log.info('>>> MyApplicationListener:{}', event); }}

還可以在同一個(gè)類中定義多個(gè)監(jiān)聽(tīng),對(duì)同一個(gè)事件的不同監(jiān)聽(tīng)還可以指定順序。order 值越小越先執(zhí)行。

package com.chenpi;import org.springframework.context.event.EventListener;import org.springframework.core.annotation.Order;import org.springframework.stereotype.Component;import lombok.extern.slf4j.Slf4j;@Slf4j@Componentpublic class MyApplicationListener01 { @Order(2) @EventListener public void onApplicationEvent(MyApplicationEvent event) {log.info('>>> onApplicationEvent order=2:{}', event); } @Order(1) @EventListener public void onApplicationEvent01(MyApplicationEvent event) {log.info('>>> onApplicationEvent order=1:{}', event); } @EventListener public void otherEvent(YourApplicationEvent event) {log.info('>>> otherEvent:{}', event); }}

執(zhí)行結(jié)果如下:

>>> onApplicationEvent order=1:MyApplicationEvent{myField=’陳皮’, source=我是事件源}

>>> onApplicationEvent order=2:MyApplicationEvent{myField=’陳皮’, source=我是事件源}

>>> otherEvent:MyApplicationEvent{myField=’我是自定義事件屬性01’, source=我是事件源01}

事件的監(jiān)聽(tīng)處理是同步的,如下:

package com.chenpi;import lombok.extern.slf4j.Slf4j;import org.springframework.context.ApplicationEventPublisher;import org.springframework.context.ApplicationEventPublisherAware;import org.springframework.stereotype.Service;@Service@Slf4jpublic class MyService implements ApplicationEventPublisherAware { private ApplicationEventPublisher applicationEventPublisher; @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.applicationEventPublisher = applicationEventPublisher; } public void testEvent() {log.info('>>> testEvent begin');applicationEventPublisher.publishEvent(new MyApplicationEvent('我是事件源', '陳皮'));applicationEventPublisher.publishEvent(new YourApplicationEvent('我是事件源01', '我是自定義事件屬性01'));log.info('>>> testEvent end'); }}

執(zhí)行結(jié)果如下:

2021-06-26 20:34:27.990  INFO 12936 --- [nio-8081-exec-1] com.chenpi.MyService                     : >>> testEvent begin

2021-06-26 20:34:27.990  INFO 12936 --- [nio-8081-exec-1] com.chenpi.MyApplicationListener01       : >>> onApplicationEvent order=1:MyApplicationEvent{myField=’陳皮’, source=我是事件源}

2021-06-26 20:34:27.991  INFO 12936 --- [nio-8081-exec-1] com.chenpi.MyApplicationListener01       : >>> onApplicationEvent order=2:MyApplicationEvent{myField=’陳皮’, source=我是事件源}

2021-06-26 20:34:27.992  INFO 12936 --- [nio-8081-exec-1] com.chenpi.MyApplicationListener01       : >>> otherEvent:MyApplicationEvent{myField=’我是自定義事件屬性01’, source=我是事件源01}

2021-06-26 20:34:27.992  INFO 12936 --- [nio-8081-exec-1] com.chenpi.MyService                     : >>> testEvent end

不過(guò),我們也可以顯示指定異步方式去執(zhí)行監(jiān)聽(tīng)器,記得在服務(wù)添加 @EnableAsync 注解開(kāi)啟異步注解。

package com.chenpi;import org.springframework.context.event.EventListener;import org.springframework.core.annotation.Order;import org.springframework.scheduling.annotation.Async;import org.springframework.stereotype.Component;import lombok.extern.slf4j.Slf4j;@Slf4j@Componentpublic class MyApplicationListener01 { @Async @Order(2) @EventListener public void onApplicationEvent(MyApplicationEvent event) {log.info('>>> onApplicationEvent order=2:{}', event); } @Order(1) @EventListener public void onApplicationEvent01(MyApplicationEvent event) {log.info('>>> onApplicationEvent order=1:{}', event); } @Async @EventListener public void otherEvent(YourApplicationEvent event) {log.info('>>> otherEvent:{}', event); }}

執(zhí)行結(jié)果如下,注意打印的線程名。

2021-06-26 20:37:04.807  INFO 9092 --- [nio-8081-exec-1] com.chenpi.MyService                     : >>> testEvent begin

2021-06-26 20:37:04.819  INFO 9092 --- [nio-8081-exec-1] com.chenpi.MyApplicationListener01       : >>> onApplicationEvent order=1:MyApplicationEvent{myField=’陳皮’, source=我是事件源}

2021-06-26 20:37:04.831  INFO 9092 --- [         task-1] com.chenpi.MyApplicationListener01       : >>> onApplicationEvent order=2:MyApplicationEvent{myField=’陳皮’, source=我是事件源}

2021-06-26 20:37:04.831  INFO 9092 --- [nio-8081-exec-1] com.chenpi.MyService                     : >>> testEvent end

2021-06-26 20:37:04.831  INFO 9092 --- [         task-2] com.chenpi.MyApplicationListener01       : >>> otherEvent:MyApplicationEvent{myField=’我是自定義事件屬性01’, source=我是事件源01}

以上就是詳解Spring事件發(fā)布與監(jiān)聽(tīng)機(jī)制的詳細(xì)內(nèi)容,更多關(guān)于Spring事件發(fā)布與監(jiān)聽(tīng)機(jī)制的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: Spring
相關(guān)文章:
主站蜘蛛池模板: 饶平县| 清新县| 长兴县| 康定县| 宝应县| 洛浦县| 垫江县| 定远县| 临夏市| 常德市| 靖边县| 连南| 乐平市| 灌阳县| 清丰县| 信阳市| 岳阳县| 喜德县| 德阳市| 禄丰县| 泾阳县| 钟山县| 海丰县| 黑山县| 阿鲁科尔沁旗| 大邑县| 安义县| 碌曲县| 南城县| 丰宁| 平原县| 遵化市| 邵武市| 宜宾县| 苏尼特左旗| 平遥县| 四子王旗| 奉贤区| 荆门市| 湘潭市| 名山县|