久久精品精选,精品九九视频,www久久只有这里有精品,亚洲熟女乱色综合一区
    分享

    Spring 中的事件機(jī)制,芳芳用過都說好~

     520jefferson 2020-11-11

    出處 http://www./Spring-Boot/Event/ 「芋道源碼」歡迎轉(zhuǎn)載,保留摘要,謝謝!

    • 1. 概述
    • 2. Spring 事件機(jī)制
    • 3. 入門示例
    • 4. Spring 內(nèi)置事件
    • 666. 彩蛋

    本文在提供完整代碼示例,可見 http://github.com/YunaiV/SpringBoot-Labs 的 lab-54 目錄。

    原創(chuàng)不易,給點(diǎn)個(gè) Star 嘿,一起沖鴨!

    1. 概述

    在設(shè)計(jì)模式中,觀察者模式是一個(gè)比較常用的設(shè)計(jì)模式。維基百科解釋如下:

    FROM http://zh./wiki/觀察者模式

    觀察者模式是軟件設(shè)計(jì)模式的一種。在此種模式中,一個(gè)目標(biāo)對(duì)象管理所有相依于它的觀察者對(duì)象,并且在它本身的狀態(tài)改變時(shí)主動(dòng)發(fā)出通知。這通常透過呼叫各觀察者所提供的方法來實(shí)現(xiàn)。

    此種模式通常被用來實(shí)時(shí)事件處理系統(tǒng)。

    在我們?nèi)粘I(yè)務(wù)開發(fā)中,觀察者模式對(duì)我們很大的一個(gè)作用,在于實(shí)現(xiàn)業(yè)務(wù)的解耦。以用戶注冊(cè)的場(chǎng)景來舉例子,假設(shè)在用戶注冊(cè)完成時(shí),需要給該用戶發(fā)送郵件、發(fā)送優(yōu)惠劵等等操作,如下圖所示:

    觀察者模式
    • UserService 在完成自身的用戶注冊(cè)邏輯之后,僅僅只需要發(fā)布一個(gè) UserRegisterEvent 事件,而無需關(guān)注其它拓展邏輯。
    • 其它 Service 可以自己訂閱 UserRegisterEvent 事件,實(shí)現(xiàn)自定義的拓展邏輯。

    友情提示:很多時(shí)候,我們會(huì)把觀察者模式發(fā)布訂閱模式放在一起對(duì)比。

    簡(jiǎn)單來說,發(fā)布訂閱模式屬于廣義上的觀察者模式,在觀察者模式的 Subject 和 Observer 的基礎(chǔ)上,引入 Event Channel 這個(gè)中介,進(jìn)一步解耦。如下圖所示:

    對(duì)比

    進(jìn)一步的討論,胖友可以瞅瞅《觀察者模式和發(fā)布訂閱模式有什么不同?》的討論。

    2. Spring 事件機(jī)制

    Spring 基于觀察者模式,實(shí)現(xiàn)了自身的事件機(jī)制,由三部分組成:

    • 事件 ApplicationEvent:通過繼承它,實(shí)現(xiàn)自定義事件。另外,通過它的 source 屬性可以獲取事件timestamp 屬性可以獲得發(fā)生時(shí)間。
    • 事件發(fā)布者 ApplicationEventPublisher:通過它,可以進(jìn)行事件的發(fā)布。
    • 事件監(jiān)聽器 ApplicationListener:通過實(shí)現(xiàn)它,進(jìn)行指定類型的事件的監(jiān)聽。

    友情提示:JDK 也內(nèi)置了事件機(jī)制的實(shí)現(xiàn),考慮到通用性,Spring 的事件機(jī)制是基于它之上進(jìn)行拓展。因此,ApplicationEvent 繼承自 java.util.EventObject,ApplicationListener 繼承自 java.util.EventListener

    3. 入門示例

    示例代碼對(duì)應(yīng)倉(cāng)庫(kù):lab-54-demo

    看完一些基礎(chǔ)的概念,我們來擼個(gè) Spring 事件機(jī)制的入門示例,具體的場(chǎng)景還是以用戶注冊(cè)為例子。新建 lab-54-demo 項(xiàng)目,最終項(xiàng)目如下圖:

    3.1 引入依賴

    pom.xml 文件中,引入相關(guān)依賴。

    <?xml version='1.0' encoding='UTF-8'?>
    <project xmlns='http://maven./POM/4.0.0'
             xmlns:xsi='http://www./2001/XMLSchema-instance'
             xsi:schemaLocation='http://maven./POM/4.0.0 http://maven./xsd/maven-4.0.0.xsd'>

        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.2.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <modelVersion>4.0.0</modelVersion>

        <artifactId>lab-54-demo</artifactId>

        <dependencies>
            <!-- 實(shí)現(xiàn)對(duì) Spring MVC 的自動(dòng)化配置 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
        </dependencies>

    </project>

    引入 spring-boot-starter-web 依賴的原因,是稍后會(huì)提供示例 API 接口,方便測(cè)試。

    3.2 UserRegisterEvent

    創(chuàng)建 UserRegisterEvent 事件類,繼承 ApplicationEvent 類,用戶注冊(cè)事件。代碼如下:

    public class UserRegisterEvent extends ApplicationEvent {

        /**
         * 用戶名
         */

        private String username;

        public UserRegisterEvent(Object source) {
            super(source);
        }

        public UserRegisterEvent(Object source, String username) {
            super(source);
            this.username = username;
        }

        public String getUsername() {
            return username;
        }

    }

    通過在 UserRegisterEvent 類中,定義成員變量 username,將用戶名附帶上。

    3.3 UserService

    創(chuàng)建 UserService 類,用戶 Service。代碼如下:

    @Service
    public class UserService implements ApplicationEventPublisherAware // <1>

        private Logger logger = LoggerFactory.getLogger(getClass());

        private ApplicationEventPublisher applicationEventPublisher;

        @Override
        public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
            this.applicationEventPublisher = applicationEventPublisher;
        }

        public void register(String username) {
            // ... 執(zhí)行注冊(cè)邏輯
            logger.info('[register][執(zhí)行用戶({}) 的注冊(cè)邏輯]', username);

            // <2> ... 發(fā)布
            applicationEventPublisher.publishEvent(new UserRegisterEvent(this, username));
        }

    }

    <1> 處,實(shí)現(xiàn) ApplicationEventPublisherAware 接口,從而將 ApplicationEventPublisher 注入到其中。

    <2> 處,在執(zhí)行完注冊(cè)邏輯后,調(diào)用 ApplicationEventPublisher 的 #publishEvent(ApplicationEvent event) 方法,發(fā)布「3.2 UserRegisterEvent」事件。

    3.4 EmailService

    創(chuàng)建 EmailService 類,郵箱 Service。代碼如下:

    @Service
    public class EmailService implements ApplicationListener<UserRegisterEvent// <1>

        private Logger logger = LoggerFactory.getLogger(getClass());

        @Override
        @Async // <3>
        public void onApplicationEvent(UserRegisterEvent event) // <2>
            logger.info('[onApplicationEvent][給用戶({}) 發(fā)送郵件]', event.getUsername());
        }

    }

    <1> 處,實(shí)現(xiàn) ApplicationListener 接口,通過 E 泛型設(shè)置感興趣的事件。

    <2> 處,實(shí)現(xiàn) #onApplicationEvent(E event) 方法,針對(duì)監(jiān)聽的 UserRegisterEvent 事件,進(jìn)行自定義處理。

    【可以不加】<3> 處,錦上添花,設(shè)置 @Async 注解,聲明異步執(zhí)行。畢竟實(shí)際場(chǎng)景下,發(fā)送郵件可能比較慢,又是非關(guān)鍵邏輯。

    友情提示:對(duì) @Async 注解感興趣的胖友,可以閱讀《芋道 Spring Boot 異步任務(wù)入門》文章。

    3.5 CouponService

    創(chuàng)建 CouponService 類,優(yōu)惠劵 Service。代碼如下:

    @Service
    public class CouponService {

        private Logger logger = LoggerFactory.getLogger(getClass());

        @EventListener // <1>
        public void addCoupon(UserRegisterEvent event) {
            logger.info('[addCoupon][給用戶({}) 發(fā)放優(yōu)惠劵]', event.getUsername());
        }

    }

    <1> 處,在方法上,添加 @EventListener 注解,并設(shè)置監(jiān)聽的事件為 UserRegisterEvent。這是另一種使用方式!

    3.6 DemoController

    創(chuàng)建 DemoController 類,提供 /demo/register 注冊(cè)接口。代碼如下:

    @RestController
    @RequestMapping('/demo')
    public class DemoController {

        @Autowired
        private UserService userService;

        @GetMapping('/register')
        public String register(String username) {
            userService.register(username);
            return 'success';
        }

    }

    3.7 DemoApplication

    創(chuàng)建 DemoApplication 類,應(yīng)用啟動(dòng)類。代碼如下:

    @SpringBootApplication
    @EnableAsync // 開啟 Spring 異步的功能
    public class DemoApplication {

        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.classargs);
        }

    }

    3.8 簡(jiǎn)單測(cè)試

    ① 執(zhí)行 DemoApplication 類,啟動(dòng)項(xiàng)目。

    ② 調(diào)用 http://127.0.0.1:8080/demo/register?username=yudaoyuanma 接口,進(jìn)行注冊(cè)。IDEA 控制臺(tái)打印日志如下:

    # UserService 發(fā)布 UserRegisterEvent 事件
    2020-04-06 13:09:39.145  INFO 18615 --- [nio-8080-exec-1] c.i.s.l.eventdemo.service.UserService    : [register][執(zhí)行用戶(yudaoyuanma) 的注冊(cè)邏輯]
    # CouponService 監(jiān)聽處理該事件
    2020-04-06 13:09:39.147  INFO 18615 --- [nio-8080-exec-1] c.i.s.l.eventdemo.service.CouponService  : [addCoupon][給用戶(yudaoyuanma) 發(fā)放優(yōu)惠劵]
    # EmailService 監(jiān)聽處理該事件
    2020-04-06 13:09:39.154  INFO 18615 --- [         task-1] c.i.s.l.eventdemo.service.EmailService   : [onApplicationEvent][給用戶(yudaoyuanma) 發(fā)送郵件]

    4. Spring 內(nèi)置事件

    在 Spring 框架中,自定義了非常多的自定義事件,讓我們更容易的進(jìn)行拓展。下面,我們來簡(jiǎn)單舉一些例子。

    4.1 ApplicationContextEvent

    ApplicationContextEvent 是 Spring Context 相關(guān)的事件基類,如下圖所示:

    友情提示:Spring Context 可以簡(jiǎn)單理解成 IoC 容器。

    ApplicationContextEvent 類圖
    • ContextStartedEvent:Spring Context 啟動(dòng)完成事件。
    • ContextStoppedEvent:Spring Context 停止完成事件。
    • ContextClosedEvent:Spring Context 停止開始事件。
    • ContextRefreshedEvent:Spring Context 初始化或刷新完成事件。

    也就是說,在 Spring Context 的整個(gè)生命周期中,會(huì)發(fā)布相應(yīng)的 ApplicationContextEvent 事件。

    SpringApplicationEvent 是 Spring Boot Application(應(yīng)用)相關(guān)的事件基類,如下圖所示:

    SpringApplicationEvent 類圖
    • ApplicationStartingEvent:Application 啟動(dòng)開始事件。
    • ApplicationEnvironmentPreparedEvent:Spring Environment 準(zhǔn)備完成的事件。
    • ApplicationContextInitializedEvent:Spring Context 準(zhǔn)備完成,但是 Bean Definition 未加載時(shí)的事件
    • ApplicationPreparedEvent:Spring Context 準(zhǔn)備完成,但是未刷新時(shí)的事件。
    • ApplicationReadyEvent:Application 啟動(dòng)成功事件。
    • ApplicationFailedEvent:Application 啟動(dòng)失敗事件。

    也就是說,在 Application 的整個(gè)生命周期中,會(huì)發(fā)布相應(yīng)的 SpringApplicationEvent 事件。

    通過 ApplicationContextEvent 和 SpringApplicationEvent 事件,我們?cè)凇队蟮?Spring Boot 持續(xù)交付 Jenkins 入門》文章的「3. 優(yōu)雅上下線」小節(jié)中,實(shí)現(xiàn)了 Spring Boot + Nginx 的優(yōu)雅上下線。

    4.2 RouteRefreshListener

    在《芋道 Spring Cloud 網(wǎng)關(guān) Spring Cloud Gateway 入門》文章的「6. 基于配置中心 Nacos 實(shí)現(xiàn)動(dòng)態(tài)路由」小節(jié)中,我們可以看到 Spring Cloud Gateway 通過監(jiān)聽 RefreshRoutesEvent 事件,結(jié)合 Nacos 作為配置中心,實(shí)現(xiàn)網(wǎng)關(guān)路由動(dòng)態(tài)刷新的功能。

    網(wǎng)關(guān)路由動(dòng)態(tài)刷新

    友情提示:Spring Cloud Zuul 也是通過監(jiān)聽 RoutesRefreshedEvent 事件,實(shí)現(xiàn)網(wǎng)關(guān)路由動(dòng)態(tài)刷新的功能。

    4.3 RefreshRemoteApplicationEvent

    在《芋道 Spring Cloud 配置中心 Spring Cloud Config 入門》文章的「5. 自動(dòng)配置刷新(第二彈)」小節(jié)中,我們可以看到 Spring Cloud Config Client 通過監(jiān)聽 RefreshRemoteApplicationEvent 事件,結(jié)合 RabbitMQ 作為 Spring Cloud Bus 消息總線,實(shí)現(xiàn)本地配置刷新的功能。

    Spring Cloud Config

    666. 彩蛋

    至此,我們已經(jīng)完成了對(duì) Spring 事件機(jī)制的學(xué)習(xí)。當(dāng)然,還有一些功能,胖友可以自己在倒騰倒騰。

    ① 如果胖友想要多個(gè)監(jiān)聽器按照指定順序執(zhí)行,可以通過實(shí)現(xiàn) Ordered 接口,指定其順序。

    ② 如果胖友想要監(jiān)聽多種 ApplicationContext 事件,可以實(shí)現(xiàn) SmartApplicationListener 接口,具體示例可以看看 SourceFilteringListener 類。

    @TransactionalEventListener 注解,可以聲明在當(dāng)前事務(wù)“結(jié)束”時(shí),執(zhí)行相應(yīng)的監(jiān)聽邏輯。

    ④ 可以通過實(shí)現(xiàn) ApplicationEventMulticaster 接口,定義自定義的事件廣播器,可以往里面添加和移除監(jiān)聽器,并發(fā)布事件給注冊(cè)在其中的監(jiān)聽器。使用比較少,基本可以忽略。

      本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
      轉(zhuǎn)藏 分享 獻(xiàn)花(0

      0條評(píng)論

      發(fā)表

      請(qǐng)遵守用戶 評(píng)論公約

      類似文章 更多

      主站蜘蛛池模板: 色欲国产精品一区成人精品| 国产午夜亚洲精品国产成人| 四虎成人在线观看免费| 99在线精品国自产拍中文字幕| 国产最新进精品视频| 国产精品一区二区AV| 日韩丝袜欧美人妻制服| 国产乱码卡二卡三卡4| 亚洲成人av免费一区| AV免费播放一区二区三区| 亚洲av男人电影天堂热app| 日本怡春院一区二区三区| 久久99国产精品尤物| 制服丝袜美腿一区二区| 精品无码一区二区三区AV| 欧美肥老太牲交大战| 亚洲午夜爱爱香蕉片| 无码天堂亚洲国产AV| 亚洲a∨国产av综合av| 隔壁人妻被水电工征服| 国产色无码专区在线观看| 亚洲AV熟妇在线观看| 亚洲av永久无码精品水牛影视 | 免费在线黄色电影| 中文字幕亚洲制服在线看| 狠狠综合久久AV一区二区| 欧美韩中文精品有码视频在线| 亚洲VA无码专区国产乱码| 亚洲色成人网站WWW永久| 天天爽夜夜爱| 在线天堂中文官网| 亚洲欧美日韩在线码| 无码中文人妻视频2019| 无码人妻精品一区二区三区下载| 久久99热只有频精品8| 欧美丰满熟妇BBB久久久| 无码A级毛片免费视频下载| 国产午夜福利视频在线| 亚洲AV伊人久久综合密臀性色| 五月天天天综合精品无码| 亚洲国产中文字幕精品|