亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

第二十七章:SpringBoot使用ApplicationEvent&Listener完成業(yè)

Reducto / 1686人閱讀

摘要:實(shí)體代碼如下所示恒宇少年碼云用戶名密碼創(chuàng)建內(nèi)添加一個(gè)注冊(cè)方法,該方法只是實(shí)現(xiàn)注冊(cè)事件發(fā)布功能,代碼如下所示恒宇少年碼云用戶注冊(cè)方法省略其他邏輯發(fā)布事件事件發(fā)布是由對(duì)象管控的,我們發(fā)布事件前需要注入對(duì)象調(diào)用方法完成事件發(fā)布。

ApplicationEvent以及Listener是Spring為我們提供的一個(gè)事件監(jiān)聽、訂閱的實(shí)現(xiàn),內(nèi)部實(shí)現(xiàn)原理是觀察者設(shè)計(jì)模式,設(shè)計(jì)初衷也是為了系統(tǒng)業(yè)務(wù)邏輯之間的解耦,提高可擴(kuò)展性以及可維護(hù)性。事件發(fā)布者并不需要考慮誰去監(jiān)聽,監(jiān)聽具體的實(shí)現(xiàn)內(nèi)容是什么,發(fā)布者的工作只是為了發(fā)布事件而已。

我們平時(shí)日常生活中也是經(jīng)常會(huì)有這種情況存在,如:我們?cè)谄綍r(shí)拔河比賽中,裁判員給我們吹響了開始的信號(hào),也就是給我們發(fā)布了一個(gè)開始的事件,而拔河雙方人員都在監(jiān)聽著這個(gè)事件,一旦事件發(fā)布后雙方人員就開始往自己方使勁。而裁判并不關(guān)心你比賽的過程,只是給你發(fā)布事件你執(zhí)行就可以了。

本章目標(biāo)

我們本章在SpringBoot平臺(tái)上通過ApplicationEvents以及Listener來完成簡單的注冊(cè)事件流程。

構(gòu)建項(xiàng)目

我們本章只是簡單的講解如何使用ApplicationEvent以及Listener來完成業(yè)務(wù)邏輯的解耦,不涉及到數(shù)據(jù)交互所以依賴需要引入的也比較少,項(xiàng)目pom.xml配置文件如下所示:

.....//省略

        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
        
            org.projectlombok
            lombok
            1.16.16
        
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
    
.....//省略

其中l(wèi)ombok依賴大家有興趣可以去深研究下,這是一個(gè)很好的工具,它可以結(jié)合Idea開發(fā)工具完成對(duì)實(shí)體的動(dòng)態(tài)添加構(gòu)造函數(shù)、Getter/Setter方法、toString方法等。

創(chuàng)建UserRegisterEvent事件

我們先來創(chuàng)建一個(gè)事件,監(jiān)聽都是圍繞著事件來掛起的。事件代碼如下所示:

package com.yuqiyu.chapter27.event;

import com.yuqiyu.chapter27.bean.UserBean;
import lombok.Getter;
import org.springframework.context.ApplicationEvent;

/**
 * ========================
 * Created with IntelliJ IDEA.
 * User:恒宇少年
 * Date:2017/7/21
 * Time:10:08
 * 碼云:http://git.oschina.net/jnyqy
 * ========================
 */
@Getter
public class UserRegisterEvent extends ApplicationEvent
{
    //注冊(cè)用戶對(duì)象
    private UserBean user;

    /**
     * 重寫構(gòu)造函數(shù)
     * @param source 發(fā)生事件的對(duì)象
     * @param user 注冊(cè)用戶對(duì)象
     */
    public UserRegisterEvent(Object source,UserBean user) {
        super(source);
        this.user = user;
    }
}

我們自定義事件UserRegisterEvent繼承了ApplicationEvent,繼承后必須重載構(gòu)造函數(shù),構(gòu)造函數(shù)的參數(shù)可以任意指定,其中source參數(shù)指的是發(fā)生事件的對(duì)象,一般我們?cè)诎l(fā)布事件時(shí)使用的是this關(guān)鍵字代替本類對(duì)象,而user參數(shù)是我們自定義的注冊(cè)用戶對(duì)象,該對(duì)象可以在監(jiān)聽內(nèi)被獲取。

在Spring內(nèi)部中有多種方式實(shí)現(xiàn)監(jiān)聽如:@EventListener注解、實(shí)現(xiàn)ApplicationListener泛型接口、實(shí)現(xiàn)SmartApplicationListener接口等,我們下面來講解下這三種方式分別如何實(shí)現(xiàn)。

創(chuàng)建UserBean

我們簡單創(chuàng)建一個(gè)用戶實(shí)體,并添加兩個(gè)字段:用戶名、密碼。實(shí)體代碼如下所示:

package com.yuqiyu.chapter27.bean;
import lombok.Data;
/**
 * ========================
 * Created with IntelliJ IDEA.
 * User:恒宇少年
 * Date:2017/7/21
 * Time:10:05
 * 碼云:http://git.oschina.net/jnyqy
 * ========================
 */
@Data
public class UserBean
{
    //用戶名
    private String name;
    //密碼
    private String password;
}
創(chuàng)建UserService

UserService內(nèi)添加一個(gè)注冊(cè)方法,該方法只是實(shí)現(xiàn)注冊(cè)事件發(fā)布功能,代碼如下所示:

package com.yuqiyu.chapter27.service;

import com.yuqiyu.chapter27.bean.UserBean;
import com.yuqiyu.chapter27.event.UserRegisterEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

/**
 * ========================
 * Created with IntelliJ IDEA.
 * User:恒宇少年
 * Date:2017/7/21
 * Time:10:11
 * 碼云:http://git.oschina.net/jnyqy
 * ========================
 */
@Service
public class UserService
{
    @Autowired
    ApplicationContext applicationContext;

    /**
     * 用戶注冊(cè)方法
     * @param user
     */
    public void register(UserBean user)
    {
        //../省略其他邏輯

        //發(fā)布UserRegisterEvent事件
        applicationContext.publishEvent(new UserRegisterEvent(this,user));
    }
}

事件發(fā)布是由ApplicationContext對(duì)象管控的,我們發(fā)布事件前需要注入ApplicationContext對(duì)象調(diào)用publishEvent方法完成事件發(fā)布。

創(chuàng)建UserController

創(chuàng)建一個(gè)@RestController控制器,對(duì)應(yīng)添加一個(gè)注冊(cè)方法簡單實(shí)現(xiàn),代碼如下所示:

package com.yuqiyu.chapter27.controller;

import com.yuqiyu.chapter27.bean.UserBean;
import com.yuqiyu.chapter27.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 用戶控制器
 * ========================
 * Created with IntelliJ IDEA.
 * User:恒宇少年
 * Date:2017/7/21
 * Time:10:05
 * 碼云:http://git.oschina.net/jnyqy
 * ========================
 */
@RestController
public class UserController
{
    //用戶業(yè)務(wù)邏輯實(shí)現(xiàn)
    @Autowired
    private UserService userService;

    /**
     * 注冊(cè)控制方法
     * @param user 用戶對(duì)象
     * @return
     */
    @RequestMapping(value = "/register")
    public String register
            (
                    UserBean user
            )
    {
        //調(diào)用注冊(cè)業(yè)務(wù)邏輯
        userService.register(user);
        return "注冊(cè)成功.";
    }
}
@EventListener實(shí)現(xiàn)監(jiān)聽

注解方式比較簡單,并不需要實(shí)現(xiàn)任何接口,具體代碼實(shí)現(xiàn)如下所示:

package com.yuqiyu.chapter27.listener;

import com.yuqiyu.chapter27.bean.UserBean;
import com.yuqiyu.chapter27.event.UserRegisterEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

/**
 * 使用@EventListener方法實(shí)現(xiàn)注冊(cè)事件監(jiān)聽
 * ========================
 * Created with IntelliJ IDEA.
 * User:恒宇少年
 * Date:2017/7/21
 * Time:10:50
 * 碼云:http://git.oschina.net/jnyqy
 * ========================
 */
@Component
public class AnnotationRegisterListener {

    /**
     * 注冊(cè)監(jiān)聽實(shí)現(xiàn)方法
     * @param userRegisterEvent 用戶注冊(cè)事件
     */
    @EventListener
    public void register(UserRegisterEvent userRegisterEvent)
    {
        //獲取注冊(cè)用戶對(duì)象
        UserBean user = userRegisterEvent.getUser();

        //../省略邏輯

        //輸出注冊(cè)用戶信息
        System.out.println("@EventListener注冊(cè)信息,用戶名:"+user.getName()+",密碼:"+user.getPassword());
    }
}

我們只需要讓我們的監(jiān)聽類被Spring所管理即可,在我們用戶注冊(cè)監(jiān)聽實(shí)現(xiàn)方法上添加@EventListener注解,該注解會(huì)根據(jù)方法內(nèi)配置的事件完成監(jiān)聽。下面我們啟動(dòng)項(xiàng)目來測(cè)試下我們事件發(fā)布時(shí)是否被監(jiān)聽者所感知。

測(cè)試事件監(jiān)聽

使用SpringBootApplication方式啟動(dòng)成功后,我們來訪問下地址:http://127.0.0.1:8080/register?name=admin&password=123456,界面輸出內(nèi)容肯定是“注冊(cè)成功”,這個(gè)是沒有問題的,我們直接查看控制臺(tái)輸出內(nèi)容,如下所示:

2017-07-21 11:09:52.532  INFO 10460 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet "dispatcherServlet"
2017-07-21 11:09:52.532  INFO 10460 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet "dispatcherServlet": initialization started
2017-07-21 11:09:52.545  INFO 10460 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet "dispatcherServlet": initialization completed in 13 ms
@EventListener注冊(cè)信息,用戶名:admin,密碼:123456

可以看到我們使用@EventListener注解配置的監(jiān)聽已經(jīng)生效了,當(dāng)我們?cè)赨serService內(nèi)發(fā)布了注冊(cè)事件時(shí),監(jiān)聽方法自動(dòng)被調(diào)用并且輸出內(nèi)信息到控制臺(tái)。

ApplicationListener實(shí)現(xiàn)監(jiān)聽

這種方式也是Spring之前比較常用的監(jiān)聽事件方式,在實(shí)現(xiàn)ApplicationListener接口時(shí)需要將監(jiān)聽事件作為泛型傳遞,監(jiān)聽實(shí)現(xiàn)代碼如下所示:

package com.yuqiyu.chapter27.listener;

import com.yuqiyu.chapter27.bean.UserBean;
import com.yuqiyu.chapter27.event.UserRegisterEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

/**
 * 原始方式實(shí)現(xiàn)
 * 用戶注冊(cè)監(jiān)聽
 * ========================
 * Created with IntelliJ IDEA.
 * User:恒宇少年
 * Date:2017/7/21
 * Time:10:24
 * 碼云:http://git.oschina.net/jnyqy
 * ========================
 */
@Component
public class RegisterListener implements ApplicationListener
{
    /**
     * 實(shí)現(xiàn)監(jiān)聽
     * @param userRegisterEvent
     */
    @Override
    public void onApplicationEvent(UserRegisterEvent userRegisterEvent) {
        //獲取注冊(cè)用戶對(duì)象
        UserBean user = userRegisterEvent.getUser();

        //../省略邏輯

        //輸出注冊(cè)用戶信息
        System.out.println("注冊(cè)信息,用戶名:"+user.getName()+",密碼:"+user.getPassword());
    }
}

我們實(shí)現(xiàn)接口后需要使用@Component注解來聲明該監(jiān)聽需要被Spring注入管理,當(dāng)有UserRegisterEvent事件發(fā)布時(shí)監(jiān)聽程序會(huì)自動(dòng)調(diào)用onApplicationEvent方法并且將UserRegisterEvent對(duì)象作為參數(shù)傳遞。
我們UserService內(nèi)的發(fā)布事件不需要修改,我們重啟下項(xiàng)目再次訪問之前的地址查看控制臺(tái)輸出的內(nèi)容如下所示:

2017-07-21 13:03:35.399  INFO 4324 --- [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet "dispatcherServlet"
2017-07-21 13:03:35.399  INFO 4324 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : FrameworkServlet "dispatcherServlet": initialization started
2017-07-21 13:03:35.411  INFO 4324 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : FrameworkServlet "dispatcherServlet": initialization completed in 12 ms
注冊(cè)信息,用戶名:admin,密碼:123456

我們看到了控制臺(tái)打印了我們監(jiān)聽內(nèi)輸出用戶信息,事件發(fā)布后就不會(huì)考慮具體哪個(gè)監(jiān)聽去處理業(yè)務(wù),甚至可以存在多個(gè)監(jiān)聽同時(shí)需要處理業(yè)務(wù)邏輯。

我們?cè)谧?cè)時(shí)如果不僅僅是記錄注冊(cè)信息到數(shù)據(jù)庫,還需要發(fā)送郵件通知用戶,當(dāng)然我們可以創(chuàng)建多個(gè)監(jiān)聽同時(shí)監(jiān)聽UserRegisterEvent事件,接下來我們先來實(shí)現(xiàn)這個(gè)需求。

郵件通知監(jiān)聽

我們使用注解的方式來完成郵件發(fā)送監(jiān)聽實(shí)現(xiàn),代碼如下所示:

package com.yuqiyu.chapter27.listener;

import com.yuqiyu.chapter27.event.UserRegisterEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

/**
 * 注冊(cè)用戶事件發(fā)送郵件監(jiān)聽
 * ========================
 * Created with IntelliJ IDEA.
 * User:恒宇少年
 * Date:2017/7/21
 * Time:13:08
 * 碼云:http://git.oschina.net/jnyqy
 * ========================
 */
@Component
public class RegisterUserEmailListener
{
    /**
     * 發(fā)送郵件監(jiān)聽實(shí)現(xiàn)
     * @param userRegisterEvent 用戶注冊(cè)事件
     */
    @EventListener
    public void sendMail(UserRegisterEvent userRegisterEvent)
    {
        System.out.println("用戶注冊(cè)成功,發(fā)送郵件。");
    }
}

監(jiān)聽編寫完成后,我們重啟項(xiàng)目,再次訪問注冊(cè)請(qǐng)求地址查看控制臺(tái)輸出內(nèi)容如下所示:

2017-07-21 13:09:20.671  INFO 7808 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet "dispatcherServlet"
2017-07-21 13:09:20.671  INFO 7808 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet "dispatcherServlet": initialization started
2017-07-21 13:09:20.685  INFO 7808 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet "dispatcherServlet": initialization completed in 14 ms
用戶注冊(cè)成功,發(fā)送郵件。
注冊(cè)信息,用戶名:admin,密碼:123456

我們看到控制臺(tái)輸出的內(nèi)容感到比較疑惑,我注冊(cè)時(shí)用戶信息寫入數(shù)據(jù)庫應(yīng)該在發(fā)送郵件前面,為什么沒有在第一步執(zhí)行呢?
好了,證明了一點(diǎn),事件監(jiān)聽是無序的,監(jiān)聽到的事件先后順序完全隨機(jī)出現(xiàn)的。我們接下來使用SmartApplicationListener實(shí)現(xiàn)監(jiān)聽方式來實(shí)現(xiàn)該邏輯。

SmartApplicationListener實(shí)現(xiàn)有序監(jiān)聽

我們對(duì)注冊(cè)用戶以及發(fā)送郵件的監(jiān)聽重新編寫,注冊(cè)用戶寫入數(shù)據(jù)庫監(jiān)聽代碼如下所示:

package com.yuqiyu.chapter27.listener;

import com.yuqiyu.chapter27.bean.UserBean;
import com.yuqiyu.chapter27.event.UserRegisterEvent;
import com.yuqiyu.chapter27.service.UserService;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component;

/**
 * 用戶注冊(cè)>>>保存用戶信息監(jiān)聽
 * ========================
 * Created with IntelliJ IDEA.
 * User:恒宇少年
 * Date:2017/7/21
 * Time:10:09
 * 碼云:http://git.oschina.net/jnyqy
 * ========================
 */
@Component
public class UserRegisterListener implements SmartApplicationListener
{
    /**
     *  該方法返回true&supportsSourceType同樣返回true時(shí),才會(huì)調(diào)用該監(jiān)聽內(nèi)的onApplicationEvent方法
     * @param aClass 接收到的監(jiān)聽事件類型
     * @return
     */
    @Override
    public boolean supportsEventType(Class aClass) {
        //只有UserRegisterEvent監(jiān)聽類型才會(huì)執(zhí)行下面邏輯
        return aClass == UserRegisterEvent.class;
    }

    /**
     *  該方法返回true&supportsEventType同樣返回true時(shí),才會(huì)調(diào)用該監(jiān)聽內(nèi)的onApplicationEvent方法
     * @param aClass
     * @return
     */
    @Override
    public boolean supportsSourceType(Class aClass) {
        //只有在UserService內(nèi)發(fā)布的UserRegisterEvent事件時(shí)才會(huì)執(zhí)行下面邏輯
        return aClass == UserService.class;
    }

    /**
     *  supportsEventType & supportsSourceType 兩個(gè)方法返回true時(shí)調(diào)用該方法執(zhí)行業(yè)務(wù)邏輯
     * @param applicationEvent 具體監(jiān)聽實(shí)例,這里是UserRegisterEvent
     */
    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {

        //轉(zhuǎn)換事件類型
        UserRegisterEvent userRegisterEvent = (UserRegisterEvent) applicationEvent;
        //獲取注冊(cè)用戶對(duì)象信息
        UserBean user = userRegisterEvent.getUser();
        //.../完成注冊(cè)業(yè)務(wù)邏輯
        System.out.println("注冊(cè)信息,用戶名:"+user.getName()+",密碼:"+user.getPassword());
    }

    /**
     * 同步情況下監(jiān)聽執(zhí)行的順序
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

SmartApplicationListener接口繼承了全局監(jiān)聽ApplicationListener,并且泛型對(duì)象使用的ApplicationEvent來作為全局監(jiān)聽,可以理解為使用SmartApplicationListener作為監(jiān)聽父接口的實(shí)現(xiàn),監(jiān)聽所有事件發(fā)布。

既然是監(jiān)聽所有的事件發(fā)布,那么SmartApplicationListener接口添加了兩個(gè)方法supportsEventType、supportsSourceType來作為區(qū)分是否是我們監(jiān)聽的事件,只有這兩個(gè)方法同時(shí)返回true時(shí)才會(huì)執(zhí)行onApplicationEvent方法。

可以看到除了上面的方法,還提供了一個(gè)getOrder方法,這個(gè)方法就可以解決執(zhí)行監(jiān)聽的順序問題,return的數(shù)值越小證明優(yōu)先級(jí)越高,執(zhí)行順序越靠前。

注冊(cè)成功發(fā)送郵件通知監(jiān)聽代碼如下所示:

package com.yuqiyu.chapter27.listener.order;

import com.yuqiyu.chapter27.bean.UserBean;
import com.yuqiyu.chapter27.event.UserRegisterEvent;
import com.yuqiyu.chapter27.service.UserService;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component;

/**
 * ========================
 * Created with IntelliJ IDEA.
 * User:恒宇少年
 * Date:2017/7/21
 * Time:13:38
 * 碼云:http://git.oschina.net/jnyqy
 * ========================
 */
@Component
public class UserRegisterSendMailListener implements SmartApplicationListener
{
    /**
     *  該方法返回true&supportsSourceType同樣返回true時(shí),才會(huì)調(diào)用該監(jiān)聽內(nèi)的onApplicationEvent方法
     * @param aClass 接收到的監(jiān)聽事件類型
     * @return
     */
    @Override
    public boolean supportsEventType(Class aClass) {
        //只有UserRegisterEvent監(jiān)聽類型才會(huì)執(zhí)行下面邏輯
        return aClass == UserRegisterEvent.class;
    }

    /**
     *  該方法返回true&supportsEventType同樣返回true時(shí),才會(huì)調(diào)用該監(jiān)聽內(nèi)的onApplicationEvent方法
     * @param aClass
     * @return
     */
    @Override
    public boolean supportsSourceType(Class aClass) {
        //只有在UserService內(nèi)發(fā)布的UserRegisterEvent事件時(shí)才會(huì)執(zhí)行下面邏輯
        return aClass == UserService.class;
    }

    /**
     *  supportsEventType & supportsSourceType 兩個(gè)方法返回true時(shí)調(diào)用該方法執(zhí)行業(yè)務(wù)邏輯
     * @param applicationEvent 具體監(jiān)聽實(shí)例,這里是UserRegisterEvent
     */
    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        //轉(zhuǎn)換事件類型
        UserRegisterEvent userRegisterEvent = (UserRegisterEvent) applicationEvent;
        //獲取注冊(cè)用戶對(duì)象信息
        UserBean user = userRegisterEvent.getUser();
        System.out.println("用戶:"+user.getName()+",注冊(cè)成功,發(fā)送郵件通知。");
    }

    /**
     * 同步情況下監(jiān)聽執(zhí)行的順序
     * @return
     */
    @Override
    public int getOrder() {
        return 1;
    }
}

在getOrder方法內(nèi)我們返回的數(shù)值為“1”,這就證明了需要在保存注冊(cè)用戶信息監(jiān)聽后執(zhí)行,下面我們重啟項(xiàng)目訪問注冊(cè)地址查看控制臺(tái)輸出內(nèi)容如下所示:

2017-07-21 13:40:43.104  INFO 10128 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet "dispatcherServlet"
2017-07-21 13:40:43.104  INFO 10128 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet "dispatcherServlet": initialization started
2017-07-21 13:40:43.119  INFO 10128 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet "dispatcherServlet": initialization completed in 15 ms
注冊(cè)信息,用戶名:admin,密碼:123456
用戶:admin,注冊(cè)成功,發(fā)送郵件通知。

這次我們看到了輸出的順序就是正確的了,先保存信息然后再發(fā)送郵件通知。

如果說我們不希望在執(zhí)行監(jiān)聽時(shí)等待監(jiān)聽業(yè)務(wù)邏輯耗時(shí),發(fā)布監(jiān)聽后立即要對(duì)接口或者界面做出反映,我們?cè)撛趺醋瞿兀?/p>

使用@Async實(shí)現(xiàn)異步監(jiān)聽

@Aysnc其實(shí)是Spring內(nèi)的一個(gè)組件,可以完成對(duì)類內(nèi)單個(gè)或者多個(gè)方法實(shí)現(xiàn)異步調(diào)用,這樣可以大大的節(jié)省等待耗時(shí)。內(nèi)部實(shí)現(xiàn)機(jī)制是線程池任務(wù)ThreadPoolTaskExecutor,通過線程池來對(duì)配置@Async的方法或者類做出執(zhí)行動(dòng)作。

線程任務(wù)池配置

我們創(chuàng)建一個(gè)ListenerAsyncConfiguration,并且使用@EnableAsync注解開啟支持異步處理,具體代碼如下所示:

package com.yuqiyu.chapter27;

import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

/**
 * 異步監(jiān)聽配置
 * ========================
 * Created with IntelliJ IDEA.
 * User:恒宇少年
 * Date:2017/7/21
 * Time:14:04
 * 碼云:http://git.oschina.net/jnyqy
 * ========================
 */
@Configuration
@EnableAsync
public class ListenerAsyncConfiguration implements AsyncConfigurer
{
    /**
     * 獲取異步線程池執(zhí)行對(duì)象
     * @return
     */
    @Override
    public Executor getAsyncExecutor() {
        //使用Spring內(nèi)置線程池任務(wù)對(duì)象
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        //設(shè)置線程池參數(shù)
        taskExecutor.setCorePoolSize(5);
        taskExecutor.setMaxPoolSize(10);
        taskExecutor.setQueueCapacity(25);
        taskExecutor.initialize();
        return taskExecutor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return null;
    }
}

我們自定義的監(jiān)聽異步配置類實(shí)現(xiàn)了AsyncConfigurer接口并且實(shí)現(xiàn)內(nèi)getAsyncExecutor方法以提供線程任務(wù)池對(duì)象的獲取。
我們只需要在異步方法上添加@Async注解就可以實(shí)現(xiàn)方法的異步調(diào)用,為了證明這一點(diǎn),我們?cè)诎l(fā)送郵件onApplicationEvent方法內(nèi)添加線程阻塞3秒,修改后的代碼如下所示:

 /**
     * supportsEventType & supportsSourceType 兩個(gè)方法返回true時(shí)調(diào)用該方法執(zhí)行業(yè)務(wù)邏輯
     * @param applicationEvent 具體監(jiān)聽實(shí)例,這里是UserRegisterEvent
     */
    @Override
    @Async
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        try {
            Thread.sleep(3000);//靜靜的沉睡3秒鐘
        }catch (Exception e)
        {
            e.printStackTrace();
        }
        //轉(zhuǎn)換事件類型
        UserRegisterEvent userRegisterEvent = (UserRegisterEvent) applicationEvent;
        //獲取注冊(cè)用戶對(duì)象信息
        UserBean user = userRegisterEvent.getUser();
        System.out.println("用戶:"+user.getName()+",注冊(cè)成功,發(fā)送郵件通知。");
    }

下面我們重啟下項(xiàng)目,訪問注冊(cè)地址,查看界面反映是否也有延遲。
我們測(cè)試發(fā)現(xiàn)訪問界面時(shí)反映速度要不之前還要快一些,我們?nèi)ゲ榭纯刂婆_(tái)時(shí),可以看到注冊(cè)信息輸出后等待3秒后再才輸出郵件發(fā)送通知,而在這之前界面已經(jīng)做出了反映。

注意:如果存在多個(gè)監(jiān)聽同一個(gè)事件時(shí),并且存在異步與同步同時(shí)存在時(shí)則不存在執(zhí)行順序。

總結(jié)

我們?cè)趥鹘y(tǒng)項(xiàng)目中往往各個(gè)業(yè)務(wù)邏輯之間耦合性較強(qiáng),因?yàn)槲覀冊(cè)趕ervice都是直接引用的關(guān)聯(lián)service或者jpa來作為協(xié)作處理邏輯,然而這種方式在后期更新、維護(hù)性難度都是大大提高了。然而我們采用事件通知、事件監(jiān)聽形式來處理邏輯時(shí)耦合性則是可以降到最小。

本章代碼已經(jīng)上傳到碼云:
網(wǎng)頁地址:http://git.oschina.net/jnyqy/lessons
Git地址:https://git.oschina.net/jnyqy/lessons.git
SpringBoot相關(guān)系列文章請(qǐng)?jiān)L問:目錄:SpringBoot學(xué)習(xí)目錄
QueryDSL相關(guān)系列文章請(qǐng)?jiān)L問:QueryDSL通用查詢框架學(xué)習(xí)目錄
感謝閱讀!
歡迎加入QQ技術(shù)交流群,共同進(jìn)步。
QQ技術(shù)交流群

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/67677.html

相關(guān)文章

  • spring ApplicationListener&ApplicationEvent

    摘要:準(zhǔn)備好了我們可以開始向中發(fā)布,當(dāng)發(fā)布后,所有在中的都會(huì)收到對(duì)應(yīng)的。將類注入到的中。測(cè)試和通過方法將發(fā)布到應(yīng)用上下文中,同時(shí)這個(gè)動(dòng)作會(huì)觸發(fā)收到事件。深入剖析發(fā)布與監(jiān)聽的過程在使用方法發(fā)布的時(shí)候,最終會(huì)調(diào)用到中的類的如下的一段代碼。 本篇主要來聊一聊spring中ApplicationListener接口和ApplicationEvent類。 從命名上可以很容易的看出來一個(gè)是listene...

    keke 評(píng)論0 收藏0
  • 《On Java 8》中文版,又名《Java 編程思想》中文第五版

    摘要:基于版本基于版本。由于中英行文差異,完全的逐字逐句翻譯會(huì)很冗余啰嗦。譯者在翻譯中同時(shí)參考了谷歌百度有道翻譯的譯文以及編程思想第四版中文版的部分內(nèi)容對(duì)其翻譯死板,生造名詞,語言精煉度差問題進(jìn)行規(guī)避和改正。 來源:LingCoder/OnJava8 主譯: LingCoder 參譯: LortSir 校對(duì):nickChenyx E-mail: 本書原作者為 [美] Bru...

    BingqiChen 評(píng)論0 收藏0
  • #yyds干貨盤點(diǎn)#安全管理制度管理辦法范文

    摘要:第四章安全管理制度發(fā)布第十條安全管理制度必須以正式文件的形式發(fā)布施行。第十一條安全管理制度由信息安全管理小組制訂,信息安全領(lǐng)導(dǎo)小組審批發(fā)布。第二十條安全管理制度的修改與廢止須經(jīng)信息安全領(lǐng)導(dǎo)組織審批確認(rèn),信息安全管理部門備案。 字?jǐn)?shù) 3610閱讀 760評(píng)論 0贊 3《xxxx安全管理制度匯編》****制度管理辦法****文...

    duan199226 評(píng)論0 收藏0
  • TRY REGEX:正則表達(dá)式交互式入門教程 翻譯&解答

    摘要:寫一個(gè)正則表達(dá)式來測(cè)試變量中是否包含字符串。用函數(shù)給出不使用字符,但和等價(jià)的正則表達(dá)式。第十四課標(biāo)志全局匹配標(biāo)志第二個(gè)常用的標(biāo)志是全局匹配標(biāo)志,用字母表示。寫出一個(gè)正則表達(dá)式來檢驗(yàn)合法性。非捕獲組的主要用途是給一個(gè)組賦予量詞。 TRY REGEX 是一個(gè)交互式的正則表達(dá)式學(xué)習(xí)項(xiàng)目項(xiàng)目地址:https://github.com/callumacra...在線地址:http://tryre...

    李義 評(píng)論0 收藏0
  • ApacheCN 人工智能知識(shí)樹 v1.0

    摘要:貢獻(xiàn)者飛龍版本最近總是有人問我,把這些資料看完一遍要用多長時(shí)間,如果你一本書一本書看的話,的確要用很長時(shí)間。為了方便大家,我就把每本書的章節(jié)拆開,再按照知識(shí)點(diǎn)合并,手動(dòng)整理了這個(gè)知識(shí)樹。 Special Sponsors showImg(https://segmentfault.com/img/remote/1460000018907426?w=1760&h=200); 貢獻(xiàn)者:飛龍版...

    劉厚水 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<