日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當(dāng)前位置:首頁 > 科技  > 軟件

使用上 Spring 的事件機(jī)制,真香!

來源: 責(zé)編: 時(shí)間:2023-09-18 21:41:51 336觀看
導(dǎo)讀本文主要是簡(jiǎn)單的講述了Spring的事件機(jī)制,基本概念,講述了事件機(jī)制的三要素事件、事件發(fā)布、事件監(jiān)聽器。如何實(shí)現(xiàn)一個(gè)事件機(jī)制,應(yīng)用的場(chǎng)景,搭配@Async注解實(shí)現(xiàn)異步的操作等等。希望對(duì)大家有所幫助。Spring的事件機(jī)制的基

本文主要是簡(jiǎn)單的講述了Spring的事件機(jī)制,基本概念,講述了事件機(jī)制的三要素事件、事件發(fā)布、事件監(jiān)聽器。如何實(shí)現(xiàn)一個(gè)事件機(jī)制,應(yīng)用的場(chǎng)景,搭配@Async注解實(shí)現(xiàn)異步的操作等等。希望對(duì)大家有所幫助。Jlh28資訊網(wǎng)——每日最新資訊28at.com

Jlh28資訊網(wǎng)——每日最新資訊28at.com

Spring的事件機(jī)制的基本概念

Spring的事件機(jī)制是Spring框架中的一個(gè)重要特性,基于觀察者模式實(shí)現(xiàn),它可以實(shí)現(xiàn)應(yīng)用程序中的解耦,提高代碼的可維護(hù)性和可擴(kuò)展性。Spring的事件機(jī)制包括事件、事件發(fā)布、事件監(jiān)聽器等幾個(gè)基本概念。其中,事件是一個(gè)抽象的概念,它代表著應(yīng)用程序中的某個(gè)動(dòng)作或狀態(tài)的發(fā)生。事件發(fā)布是事件發(fā)生的地方,它負(fù)責(zé)產(chǎn)生事件并通知事件監(jiān)聽器。事件監(jiān)聽器是事件的接收者,它負(fù)責(zé)處理事件并執(zhí)行相應(yīng)的操作。在Spring的事件機(jī)制中,事件源和事件監(jiān)聽器之間通過事件進(jìn)行通信,從而實(shí)現(xiàn)了模塊之間的解耦。Jlh28資訊網(wǎng)——每日最新資訊28at.com

舉個(gè)例子:用戶修改密碼,修改完密碼后需要短信通知用戶,記錄關(guān)鍵性日志,等等其他業(yè)務(wù)操作。Jlh28資訊網(wǎng)——每日最新資訊28at.com

如下圖,就是我們需要調(diào)用多個(gè)服務(wù)來進(jìn)行實(shí)現(xiàn)一個(gè)修改密碼的功能。Jlh28資訊網(wǎng)——每日最新資訊28at.com

Jlh28資訊網(wǎng)——每日最新資訊28at.com

 使用了事件機(jī)制后,我們只需要發(fā)布一個(gè)事件,無需關(guān)心其擴(kuò)展的邏輯,讓我們的事件監(jiān)聽器去處理,從而實(shí)現(xiàn)了模塊之間的解耦。Jlh28資訊網(wǎng)——每日最新資訊28at.com

Jlh28資訊網(wǎng)——每日最新資訊28at.com

 事件

通過繼承ApplicationEvent,實(shí)現(xiàn)自定義事件。是對(duì) Java EventObject 的擴(kuò)展,表示 Spring 的事件,Spring 中的所有事件都要基于其進(jìn)行擴(kuò)展。其源碼如下。Jlh28資訊網(wǎng)——每日最新資訊28at.com

我們可以獲取到timestamp屬性指的是發(fā)生時(shí)間。Jlh28資訊網(wǎng)——每日最新資訊28at.com

Jlh28資訊網(wǎng)——每日最新資訊28at.com

 事件發(fā)布

Jlh28資訊網(wǎng)——每日最新資訊28at.com

事件發(fā)布是事件發(fā)生的地方,它負(fù)責(zé)產(chǎn)生事件并通知事件監(jiān)聽器。ApplicationEventPublisher用于用于發(fā)布 ApplicationEvent 事件,發(fā)布后 ApplicationListener 才能監(jiān)聽到事件進(jìn)行處理。源碼如下。Jlh28資訊網(wǎng)——每日最新資訊28at.com

需要一個(gè)ApplicationEvent,就是我們的事件,來進(jìn)行發(fā)布事件。Jlh28資訊網(wǎng)——每日最新資訊28at.com

Jlh28資訊網(wǎng)——每日最新資訊28at.com

 事件監(jiān)聽器

ApplicationListener 是 Spring 事件的監(jiān)聽器,用來接受事件,所有的監(jiān)聽器都必須實(shí)現(xiàn)該接口。該接口源碼如下。Jlh28資訊網(wǎng)——每日最新資訊28at.com

Jlh28資訊網(wǎng)——每日最新資訊28at.com

 Spring的事件機(jī)制的使用方法

Jlh28資訊網(wǎng)——每日最新資訊28at.com

下面會(huì)給大家演示如何去使用Spring的事件機(jī)制。就拿修改密碼作為演示。Jlh28資訊網(wǎng)——每日最新資訊28at.com

Jlh28資訊網(wǎng)——每日最新資訊28at.com

如何定義一個(gè)事件

新增一個(gè)類,繼承我們的ApplicationEvent。Jlh28資訊網(wǎng)——每日最新資訊28at.com

如下面代碼,繼承后定義了一個(gè)userId,有一個(gè)UserChangePasswordEvent方法。這里就定義我們監(jiān)聽器需要的業(yè)務(wù)參數(shù),監(jiān)聽器需要那些參數(shù),我們這里就定義那些參數(shù)。Jlh28資訊網(wǎng)——每日最新資訊28at.com

/** * @Author JiaQIng * @Description 修改密碼事件 * @ClassName UserChangePasswordEvent * @Date 2023/3/26 13:55 **/@Getter@Setterpublic class UserChangePasswordEvent extends ApplicationEvent {    private String userId;    public UserChangePasswordEvent(String userId) {        super(new Object());        this.userId = userId;    }}

Jlh28資訊網(wǎng)——每日最新資訊28at.com

如何監(jiān)聽事件

實(shí)現(xiàn)監(jiān)聽器有兩種方法:Jlh28資訊網(wǎng)——每日最新資訊28at.com

(1) 新建一個(gè)類實(shí)現(xiàn)ApplicationListener接口,并且重寫onApplicationEvent方法注入到Spring容器中,交給Spring管理如下代碼新建了一個(gè)發(fā)送短信監(jiān)聽器,收到事件后執(zhí)行業(yè)務(wù)操作****;Jlh28資訊網(wǎng)——每日最新資訊28at.com

/** * @Author JiaQIng * @Description 發(fā)送短信監(jiān)聽器 * @ClassName MessageListener * @Date 2023/3/26 14:16 **/@Componentpublic class MessageListener implements ApplicationListener<UserChangePasswordEvent> {    @Override    public void onApplicationEvent(UserChangePasswordEvent event) {        System.out.println("收到事件:" + event);        System.out.println("開始執(zhí)行業(yè)務(wù)操作給用戶發(fā)送短信。用戶userId為:" + event.getUserId());    }}

(2) 使用@EventListener注解標(biāo)注處理事件的方法,此時(shí)Spring將創(chuàng)建一個(gè)ApplicationListenerbean對(duì)象,使用給定的方法處理事件源碼如下參數(shù)可以給指定的事件這里巧妙的用到了@AliasFor的能力,放到了@EventListener身上注意:一般建議都需要指定此值,否則默認(rèn)可以處理所有類型的事件,范圍太廣了;Jlh28資訊網(wǎng)——每日最新資訊28at.com

Jlh28資訊網(wǎng)——每日最新資訊28at.com

 代碼如下。新建一個(gè)事件監(jiān)聽器,注入到Spring容器中,交給Spring管理。在指定方法上添加@EventListener參數(shù)為監(jiān)聽的事件。方法為業(yè)務(wù)代碼。使用 @EventListener 注解的好處是一個(gè)類可以寫很多監(jiān)聽器,定向監(jiān)聽不同的事件,或者同一個(gè)事件。Jlh28資訊網(wǎng)——每日最新資訊28at.com

/** * @Author JiaQIng * @Description 事件監(jiān)聽器 * @ClassName LogListener * @Date 2023/3/26 14:22 **/@Componentpublic class ListenerEvent {    @EventListener({ UserChangePasswordEvent.class })    public void LogListener(UserChangePasswordEvent event) {        System.out.println("收到事件:" + event);        System.out.println("開始執(zhí)行業(yè)務(wù)操作生成關(guān)鍵日志。用戶userId為:" + event.getUserId());    }    @EventListener({ UserChangePasswordEvent.class })    public void messageListener(UserChangePasswordEvent event) {        System.out.println("收到事件:" + event);        System.out.println("開始執(zhí)行業(yè)務(wù)操作給用戶發(fā)送短信。用戶userId為:" + event.getUserId());    }}

(3)  @TransactionalEventListener來定義一個(gè)監(jiān)聽器,他與@EventListener不同的就是@EventListener標(biāo)記一個(gè)方法作為監(jiān)聽器,他默認(rèn)是同步執(zhí)行,如果發(fā)布事件的方法處于事務(wù)中,那么事務(wù)會(huì)在監(jiān)聽器方法執(zhí)行完畢之后才提交事件發(fā)布之后就由監(jiān)聽器去處理,而不要影響原有的事務(wù),也就是說希望事務(wù)及時(shí)提交我們就可以使用該注解來標(biāo)識(shí)注意此注解需要spring-tx的依賴;Jlh28資訊網(wǎng)——每日最新資訊28at.com

注解源碼如下:主要是看一下注釋內(nèi)容。Jlh28資訊網(wǎng)——每日最新資訊28at.com

// 在這個(gè)注解上面有一個(gè)注解:@EventListener,所以表明其實(shí)這個(gè)注解也是個(gè)事件監(jiān)聽器。 @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@EventListenerpublic @interface TransactionalEventListener { /**  * 這個(gè)注解取值有:BEFORE_COMMIT(指定目標(biāo)方法在事務(wù)commit之前執(zhí)行)、AFTER_COMMIT(指定目標(biāo)方法在事務(wù)commit之后執(zhí)行)、  * AFTER_ROLLBACK(指定目標(biāo)方法在事務(wù)rollback之后執(zhí)行)、AFTER_COMPLETION(指定目標(biāo)方法在事務(wù)完成時(shí)執(zhí)行,這里的完成是指無論事務(wù)是成功提交還是事務(wù)回滾了)  * 各個(gè)值都代表什么意思表達(dá)什么功能,非常清晰,  * 需要注意的是:AFTER_COMMIT + AFTER_COMPLETION是可以同時(shí)生效的  * AFTER_ROLLBACK + AFTER_COMPLETION是可以同時(shí)生效的  */ TransactionPhase phase() default TransactionPhase.AFTER_COMMIT; /**  * 表明若沒有事務(wù)的時(shí)候,對(duì)應(yīng)的event是否需要執(zhí)行,默認(rèn)值為false表示,沒事務(wù)就不執(zhí)行了。  */ boolean fallbackExecution() default false; /**  *  這里巧妙的用到了@AliasFor的能力,放到了@EventListener身上  *  注意:一般建議都需要指定此值,否則默認(rèn)可以處理所有類型的事件,范圍太廣了。  */ @AliasFor(annotation = EventListener.class, attribute = "classes") Class<?>[] value() default {}; /**  * The event classes that this listener handles.  * <p>If this attribute is specified with a single value, the annotated  * method may optionally accept a single parameter. However, if this  * attribute is specified with multiple values, the annotated method  * must <em>not</em> declare any parameters.  */ @AliasFor(annotation = EventListener.class, attribute = "classes") Class<?>[] classes() default {}; /**  * Spring Expression Language (SpEL) attribute used for making the event  * handling conditional.  * <p>The default is {@code ""}, meaning the event is always handled.  * @see EventListener#condition  */ @AliasFor(annotation = EventListener.class, attribute = "condition") String condition() default ""; /**  * An optional identifier for the listener, defaulting to the fully-qualified  * signature of the declaring method (e.g. "mypackage.MyClass.myMethod()").  * @since 5.3  * @see EventListener#id  * @see TransactionalApplicationListener#getListenerId()  */ @AliasFor(annotation = EventListener.class, attribute = "id") String id() default "";}

使用方式如下。phase事務(wù)類型,value指定事件。Jlh28資訊網(wǎng)——每日最新資訊28at.com

/** * @Author JiaQIng * @Description 事件監(jiān)聽器 * @ClassName LogListener * @Date 2023/3/26 14:22 **/@Componentpublic class ListenerEvent {    @EventListener({ UserChangePasswordEvent.class })    public void logListener(UserChangePasswordEvent event) {        System.out.println("收到事件:" + event);        System.out.println("開始執(zhí)行業(yè)務(wù)操作生成關(guān)鍵日志。用戶userId為:" + event.getUserId());    }    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT,value = { UserChangePasswordEvent.class })    public void messageListener(UserChangePasswordEvent event) {        System.out.println("收到事件:" + event);        System.out.println("開始執(zhí)行業(yè)務(wù)操作給用戶發(fā)送短信。用戶userId為:" + event.getUserId());    }}

Jlh28資訊網(wǎng)——每日最新資訊28at.com

如何發(fā)布一個(gè)事件

(1) 使用ApplicationContext進(jìn)行發(fā)布,由于ApplicationContext已經(jīng)繼承了ApplicationEventPublisher,因此可以直接使用發(fā)布事件源碼如下;Jlh28資訊網(wǎng)——每日最新資訊28at.com

Jlh28資訊網(wǎng)——每日最新資訊28at.com

 (2) 直接注入我們的ApplicationEventPublisher,使用@Autowired注入一下;Jlh28資訊網(wǎng)——每日最新資訊28at.com

三種發(fā)布事件的方法,我給大家演示一下@Autowired注入的方式發(fā)布我們的事件。Jlh28資訊網(wǎng)——每日最新資訊28at.com

@SpringBootTestclass SpirngEventApplicationTests {    @Autowired    ApplicationEventPublisher appEventPublisher;    @Test    void contextLoads() {        appEventPublisher.publishEvent(new UserChangePasswordEvent("1111111"));    }}

我們執(zhí)行一下看一下接口:Jlh28資訊網(wǎng)——每日最新資訊28at.com

Jlh28資訊網(wǎng)——每日最新資訊28at.com

 測(cè)試成功。Jlh28資訊網(wǎng)——每日最新資訊28at.com

Jlh28資訊網(wǎng)——每日最新資訊28at.com

搭配@Async注解實(shí)現(xiàn)異步操作

監(jiān)聽器默認(rèn)是同步執(zhí)行的,如果我們想實(shí)現(xiàn)異步執(zhí)行,可以搭配@Async注解使用,但是前提條件是你真的懂@Async注解,使用不當(dāng)會(huì)出現(xiàn)問題的。 后續(xù)我會(huì)出一篇有關(guān)@Async注解使用的文章。這里就不給大家詳細(xì)的解釋了。有想了解的同學(xué)可以去網(wǎng)上學(xué)習(xí)一下有關(guān)@Async注解使用。Jlh28資訊網(wǎng)——每日最新資訊28at.com

使用@Async時(shí),需要配置線程池,否則用的還是默認(rèn)的線程池也就是主線程池,線程池使用不當(dāng)會(huì)浪費(fèi)資源,嚴(yán)重的會(huì)出現(xiàn)OOM事故。Jlh28資訊網(wǎng)——每日最新資訊28at.com

下圖是阿里巴巴開發(fā)手冊(cè)的強(qiáng)制要求。Jlh28資訊網(wǎng)——每日最新資訊28at.com

Jlh28資訊網(wǎng)——每日最新資訊28at.com

 簡(jiǎn)單的演示一下:這里聲明一下俺沒有使用線程池,只是簡(jiǎn)單的演示一下。Jlh28資訊網(wǎng)——每日最新資訊28at.com

(1) 在我們的啟動(dòng)類上添加@EnableAsync開啟異步執(zhí)行配置;Jlh28資訊網(wǎng)——每日最新資訊28at.com

@EnableAsync@SpringBootApplicationpublic class SpirngEventApplication {    public static void main(String[] args) {        SpringApplication.run(SpirngEventApplication.class, args);    }}

(2) 在我們想要異步執(zhí)行的監(jiān)聽器上添加@Async注解:Jlh28資訊網(wǎng)——每日最新資訊28at.com

/** * @Author JiaQIng * @Description 事件監(jiān)聽器 * @ClassName LogListener * @Date 2023/3/26 14:22 **/@Componentpublic class ListenerEvent {        @Async    @EventListener({ UserChangePasswordEvent.class })    public void logListener(UserChangePasswordEvent event) {        System.out.println("收到事件:" + event);        System.out.println("開始執(zhí)行業(yè)務(wù)操作生成關(guān)鍵日志。用戶userId為:" + event.getUserId());    }}

這樣我們的異步執(zhí)行監(jiān)聽器的業(yè)務(wù)操作就完成了。Jlh28資訊網(wǎng)——每日最新資訊28at.com

Jlh28資訊網(wǎng)——每日最新資訊28at.com

Spring的事件機(jī)制的應(yīng)用場(chǎng)景

  • 告警操作,比喻釘釘告警,異常告警,可以通過事件機(jī)制進(jìn)行解耦;
  • 關(guān)鍵性日志記錄和業(yè)務(wù)埋點(diǎn),比喻說我們的關(guān)鍵日志需要入庫,記錄一下操作時(shí)間,操作人,變更內(nèi)容等等,可以通過事件機(jī)制進(jìn)行解耦;
  • 性能監(jiān)控,比喻說一些接口的時(shí)長(zhǎng),性能方便的埋點(diǎn)等可以通過事件機(jī)制進(jìn)行解耦;
  • 一切與主業(yè)務(wù)無關(guān)的操作都可以通過這種方式進(jìn)行解耦,常用的場(chǎng)景大概就上述提到的,而且很多架構(gòu)的源碼都有使用這種機(jī)制,如GateWay,Spring等等;

Jlh28資訊網(wǎng)——每日最新資訊28at.com

Spring的事件機(jī)制的注意事項(xiàng)

  • 對(duì)于同一個(gè)事件,有多個(gè)監(jiān)聽器的時(shí)候,注意可以通過@Order注解指定順序,Order的value值越小,執(zhí)行的優(yōu)先級(jí)就越高;
  • 如果發(fā)布事件的方法處于事務(wù)中,那么事務(wù)會(huì)在監(jiān)聽器方法執(zhí)行完畢之后才提交事件發(fā)布之后就由監(jiān)聽器去處理,而不要影響原有的事務(wù),也就是說希望事務(wù)及時(shí)提交我們就可以@TransactionalEventListener來定義一個(gè)監(jiān)聽器;
  • 監(jiān)聽器默認(rèn)是同步執(zhí)行的,如果我們想實(shí)現(xiàn)異步執(zhí)行,可以搭配@Async注解使用,但是前提條件是你真的懂@Async注解,使用不當(dāng)會(huì)出現(xiàn)問題的;
  • 對(duì)于同一個(gè)事件,有多個(gè)監(jiān)聽器的時(shí)候,如果出現(xiàn)了異常,后續(xù)的監(jiān)聽器就失效了,因?yàn)樗前淹粋€(gè)事件的監(jiān)聽器add在一個(gè)集合里面循環(huán)執(zhí)行,如果出現(xiàn)異常,需要注意捕獲異常處理異常;

本文鏈接:http://www.www897cc.com/showinfo-26-10471-0.html使用上 Spring 的事件機(jī)制,真香!

聲明:本網(wǎng)頁內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com

上一篇: 拋磚引玉:Redis 與 接口自動(dòng)化測(cè)試框架的結(jié)合

下一篇: Python應(yīng)知小技巧:如何用更少的代碼做更多的事情

標(biāo)簽:
  • 熱門焦點(diǎn)
Top 主站蜘蛛池模板: 浦北县| 克什克腾旗| 绍兴县| 新乡县| 盐城市| 正安县| 邳州市| 苏州市| 克东县| 平昌县| 安西县| 郁南县| 玛曲县| 揭东县| 新巴尔虎右旗| 山东| 龙川县| 秦皇岛市| 黎川县| 麦盖提县| 买车| 万盛区| 赣州市| 静宁县| 乌拉特前旗| 安溪县| 合阳县| 宁晋县| 柞水县| 许昌县| 台安县| 扎兰屯市| 乌拉特中旗| 墨竹工卡县| 永福县| 子长县| 来宾市| 萨嘎县| 新密市| 金堂县| 鹤峰县|