22《Spring Boot 入門教程》Spring Boot AOP 應(yīng)用場景

1. 前言

Spring 最重要的兩個功能倘潜,就是依賴注入(DI)和面向切面編程 (AOP)格遭。

AOP 為我們提供了處理問題的全局化視角愚铡,使用得當(dāng)可以極大提高編程效率傍妒。

Spring Boot 中使用 AOP 與 Spring 中使用 AOP 幾乎沒有什么區(qū)別幔摸,只是建議盡量使用 Java 配置代替 XML 配置。

本節(jié)就來演示下 Spring Boot 中使用 AOP 的常見應(yīng)用場景颤练。

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

首先我們需要構(gòu)建一個 Spring Boot 項(xiàng)目并引入 AOP 依賴既忆,后續(xù)場景演示均是在這個項(xiàng)目上實(shí)現(xiàn)的。

2.1 使用 Spring Initializr 創(chuàng)建項(xiàng)目

Spring Boot 版本選擇 2.2.5 嗦玖,Group 為 com.imooc 患雇, Artifact 為 spring-boot-aop,生成項(xiàng)目后導(dǎo)入 Eclipse 開發(fā)環(huán)境宇挫。

2.2 引入項(xiàng)目依賴

我們引入 Web 項(xiàng)目依賴與 AOP 依賴苛吱。

實(shí)例:

        <!-- Web項(xiàng)目依賴 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- AOP -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

2.3 新建控制層、服務(wù)層器瘪、數(shù)據(jù)訪問層

為了便于后續(xù)的演示翠储,我們依次新建控制類、服務(wù)類橡疼、數(shù)據(jù)訪問類援所,并將其放入對應(yīng)的包中,項(xiàng)目結(jié)構(gòu)如下:


5ecc734c09cb084907430182.jpg

項(xiàng)目結(jié)構(gòu)

各個類代碼如下欣除,注意此處僅僅是為了演示 AOP 的使用任斋,并未真實(shí)訪問數(shù)據(jù)庫,而是直接返回了測試數(shù)據(jù)。

實(shí)例:

/**
 * 商品控制器類
 */
@RestController
public class GoodsController {
    @Autowired
    private GoodsService goodsService;

    /**
     * 獲取商品列表
     */
    @GetMapping("/goods")
    public List getList() {
        return goodsService.getList();
    }
}

實(shí)例:

/**
 * 商品服務(wù)類
 */
@Service
public class GoodsService {
    @Autowired
    private GoodsDao goodsDao;

    /**
     * 獲取商品信息列表
     */
    public List getList() {
        return goodsDao.getList();
    }
}

實(shí)例:

/**
 * 商品數(shù)據(jù)庫訪問類
 */
@Repository // 標(biāo)注數(shù)據(jù)訪問類
public class GoodsDao {
    /**
     * 查詢商品列表
     */
    public List getList() {
        return new ArrayList();
    }
}

3. 使用 AOP 記錄日志

如果要記錄對控制器接口的訪問日志废酷,可以定義一個切面瘟檩,切入點(diǎn)即為控制器中的接口方法,然后通過前置通知來打印日志澈蟆。

實(shí)例:

/**
 * 日志切面
 */
@Component
@Aspect // 標(biāo)注為切面
public class LogAspect {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    // 切入點(diǎn)表達(dá)式墨辛,表示切入點(diǎn)為控制器包中的所有方法
    @Pointcut("within(com.imooc.springbootaop.controller..*)")
    public void LogAspect() {
    }

    // 切入點(diǎn)之前執(zhí)行
    @Before("LogAspect()")
    public void doBefore(JoinPoint joinPoint) {
        logger.info("訪問時間:{}--訪問接口:{}", new Date(), joinPoint.getSignature());
    }
}

啟動項(xiàng)目后,訪問控制器中的方法之前會先執(zhí)行 doBefore 方法趴俘《么兀控制臺打印如下:

2020-05-25 22:14:12.317  INFO 9992 --- [nio-8080-exec-2] com.imooc.springbootaop.LogAspect        : 
訪問時間:Mon May 25 22:14:12 CST 2020--訪問接口:List com.imooc.springbootaop.controller.GoodsController.getList()

4. 使用 AOP 監(jiān)控性能

在研發(fā)項(xiàng)目的性能測試階段,或者項(xiàng)目部署后寥闪,我們會希望查看服務(wù)層方法執(zhí)行的時間太惠。以便精準(zhǔn)的了解項(xiàng)目中哪些服務(wù)方法執(zhí)行速度慢,后續(xù)可以針對性的進(jìn)行性能優(yōu)化疲憋。

此時我們就可以使用 AOP 的環(huán)繞通知凿渊,監(jiān)控服務(wù)方法的執(zhí)行時間。

實(shí)例:

/**
 * 服務(wù)層方法切面
 */
@Component
@Aspect // 標(biāo)注為切面
public class ServiceAspect {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    // 切入點(diǎn)表達(dá)式缚柳,表示切入點(diǎn)為服務(wù)層包中的所有方法
    @Pointcut("within(com.imooc.springbootaop.service..*)")
    public void ServiceAspect() {
    }

    @Around("ServiceAspect()") // 環(huán)繞通知
    public Object deAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();// 記錄開始時間
        Object result = joinPoint.proceed();
        logger.info("服務(wù)層方法:{}--執(zhí)行時間:{}毫秒", joinPoint.getSignature(), System.currentTimeMillis() - startTime);
        return result;
    }
}

當(dāng)服務(wù)層方法被調(diào)用時埃脏,控制臺輸入日志如下:

2020-05-25 22:25:56.830  INFO 4800 --- [nio-8080-exec-1] com.imooc.springbootaop.ServiceAspect    : 
服務(wù)層方法:List com.imooc.springbootaop.service.GoodsService.getList()--執(zhí)行時間:3毫秒

Tips:正常情況下,用戶查看頁面或進(jìn)行更新操作時秋忙,耗時超過 1.5 秒彩掐,就會感覺到明顯的遲滯感。由于前后端交互也需要耗時灰追,按正態(tài)分布的話堵幽,大部分交互耗時在 0.4秒 左右。所以在我參與的項(xiàng)目中弹澎,會對耗時超過 1.1 秒的服務(wù)層方法進(jìn)行跟蹤分析谐檀,通過優(yōu)化 SQL 語句、優(yōu)化算法裁奇、添加緩存等方式縮短方法執(zhí)行時間桐猬。上面的數(shù)值均為我個人的經(jīng)驗(yàn)參考值,還要視乎具體的服務(wù)器刽肠、網(wǎng)絡(luò)溃肪、應(yīng)用場景來確定合理的監(jiān)控臨界值。

5. 使用 AOP 統(tǒng)一后端返回值格式

前后端分離的項(xiàng)目結(jié)構(gòu)中音五,前端通過 Ajax 請求后端接口惫撰,此時最好使用統(tǒng)一的返回值格式供前端處理。此處就可以借助 AOP 來實(shí)現(xiàn)正常情況躺涝、異常情況返回值的格式統(tǒng)一厨钻。

5.1 定義返回值類

首先定義返回值類,它屬于業(yè)務(wù)邏輯對象 (Bussiness Object),所以此處命名為 ResultBo 夯膀,代碼如下:

實(shí)例:

public class ResultBo<T> {
    /**
     * 錯誤碼 0表示沒有錯誤(異常) 其他數(shù)字代表具體錯誤碼
     */
    private int code;
    /**
     * 后端返回消息
     */
    private String msg;
    /**
     * 后端返回的數(shù)據(jù)
     */
    private T data;
    /**
     * 無參數(shù)構(gòu)造函數(shù)
     */
    public ResultBo() {
        this.code = 0;
        this.msg = "操作成功";
    }
    /**
     * 帶數(shù)據(jù)data構(gòu)造函數(shù)
     */
    public ResultBo(T data) {
        this();
        this.data = data;
    }
    /**
     * 存在異常的構(gòu)造函數(shù)
     */
    public ResultBo(Exception ex) {
        this.code = 99999;// 其他未定義異常
        this.msg = ex.getMessage();
    }
    // 省略 get set
}

5.2 修改控制層返回值類型

對所有的控制層方法進(jìn)行修改诗充,保證返回值均通過 ResultBo 包裝,另外我們再定義一個方法诱建,模擬拋出異常的控制層方法蝴蜓。

實(shí)例:

    /**
     * 獲取商品列表
     */
    @GetMapping("/goods")
    public ResultBo getList() {
        return new ResultBo(goodsService.getList());
    }
    /**
     * 模擬拋出異常的方法
     */
    @GetMapping("/test")
    public ResultBo test() {
        int a = 1 / 0;
        return new ResultBo(goodsService.getList());
    }

5.3 定義切面處理異常返回值

正常控制層方法都返回 ResultBo 類型對象俺猿,然后我們需要定義切面茎匠,處理控制層拋出的異常。當(dāng)發(fā)生異常時押袍,同樣返回 ResultBo 類型的對象诵冒,并且對象中包含異常信息。

實(shí)例:

/**
 * 返回值切面
 */
@Component
@Aspect
public class ResultAspect {
    // 切入點(diǎn)表達(dá)式谊惭,表示切入點(diǎn)為返回類型ResultBo的所有方法
    @Pointcut("execution(public com.imooc.springbootaop.ResultBo *(..))")
    public void ResultAspect() {
    }

    // 環(huán)繞通知
    @Around("ResultAspect()")
    public Object deAround(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            return joinPoint.proceed();// 返回正常結(jié)果
        } catch (Exception ex) {
            return new ResultBo<>(ex);// 被切入的方法執(zhí)行異常時汽馋,返回ResultBo
        }
    }
}

5.4 測試

啟動項(xiàng)目,訪問 http://127.0.0.1:8080/goods 返回數(shù)據(jù)如下:

實(shí)例:

{"code":0,"msg":"操作成功","data":[]}

然后訪問 http://127.0.0.1:8080/test 午笛,返回數(shù)據(jù)如下:

實(shí)例:

{"code":99999,"msg":"/ by zero","data":null}

這樣惭蟋,前端可以根據(jù)返回值的 code苗桂, 來判斷后端是否正常響應(yīng)药磺。如果 code 為 0 ,則進(jìn)行正常業(yè)務(wù)邏輯操作煤伟;如果 code 非 0 癌佩,則可以彈窗顯示 msg 提示信息。

6. 小結(jié)

AOP 之所以如此重要便锨,在于它提供了解決問題的新視角围辙。通過將業(yè)務(wù)邏輯抽象出切面,功能代碼可以切入指定位置放案,從而消除重復(fù)的模板代碼姚建。

使用 AOP 有一種掌握全局的快感,發(fā)現(xiàn)業(yè)務(wù)邏輯中的切面頗有一番趣味吱殉,希望大家都能多多體會掸冤,編程且快樂著應(yīng)該是我輩的追求。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末友雳,一起剝皮案震驚了整個濱河市稿湿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌押赊,老刑警劉巖饺藤,帶你破解...
    沈念sama閱讀 212,080評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡涕俗,警方通過查閱死者的電腦和手機(jī)罗丰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,422評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來咽袜,“玉大人丸卷,你說我怎么就攤上這事⊙玻” “怎么了谜嫉?”我有些...
    開封第一講書人閱讀 157,630評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長凹联。 經(jīng)常有香客問我沐兰,道長,這世上最難降的妖魔是什么蔽挠? 我笑而不...
    開封第一講書人閱讀 56,554評論 1 284
  • 正文 為了忘掉前任住闯,我火速辦了婚禮,結(jié)果婚禮上澳淑,老公的妹妹穿的比我還像新娘比原。我一直安慰自己,他們只是感情好杠巡,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,662評論 6 386
  • 文/花漫 我一把揭開白布量窘。 她就那樣靜靜地躺著,像睡著了一般氢拥。 火紅的嫁衣襯著肌膚如雪蚌铜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,856評論 1 290
  • 那天嫩海,我揣著相機(jī)與錄音冬殃,去河邊找鬼。 笑死叁怪,一個胖子當(dāng)著我的面吹牛审葬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播奕谭,決...
    沈念sama閱讀 39,014評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼涣觉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了展箱?” 一聲冷哼從身側(cè)響起旨枯,我...
    開封第一講書人閱讀 37,752評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎混驰,沒想到半個月后攀隔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體皂贩,經(jīng)...
    沈念sama閱讀 44,212評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,541評論 2 327
  • 正文 我和宋清朗相戀三年昆汹,在試婚紗的時候發(fā)現(xiàn)自己被綠了明刷。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,687評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡满粗,死狀恐怖辈末,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情映皆,我是刑警寧澤挤聘,帶...
    沈念sama閱讀 34,347評論 4 331
  • 正文 年R本政府宣布,位于F島的核電站捅彻,受9級特大地震影響组去,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜步淹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,973評論 3 315
  • 文/蒙蒙 一从隆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧缭裆,春花似錦键闺、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,777評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至盅藻,卻和暖如春购桑,著一層夾襖步出監(jiān)牢的瞬間畅铭,已是汗流浹背氏淑。 一陣腳步聲響...
    開封第一講書人閱讀 32,006評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留硕噩,地道東北人假残。 一個月前我還...
    沈念sama閱讀 46,406評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像炉擅,于是被迫代替她去往敵國和親辉懒。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,576評論 2 349

推薦閱讀更多精彩內(nèi)容