欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring AOP 的組成和實(shí)現(xiàn)

 更新時(shí)間:2023年07月31日 10:16:53   作者:小魚的學(xué)習(xí)筆記  
這篇文章主要介紹了Spring AOP 的組成和實(shí)現(xiàn),AOP 是一種思想,Spring AOP 是這種思想的具體實(shí)現(xiàn),本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下

1. Spring AOP 簡(jiǎn)介

AOP 是一種思想,Spring AOP 是這種思想的具體實(shí)現(xiàn)。

OOP:面向?qū)ο缶幊?/p>

AOP:面向切面編程

AOP 面向切面編程,就是對(duì)某一類事情的集中處理。

比如,我們需要在 CSDN 上進(jìn)行編輯博客、發(fā)布博客、刪除博客等操作,這些功能都是需要進(jìn)行權(quán)限校驗(yàn)的,判斷是否登錄

開發(fā)三階段

對(duì)于公共方法的處理:

  • (初級(jí)階段)每個(gè)方法都去實(shí)現(xiàn)
  • (中級(jí)階段)把同一類功能抽取成公共方法
  • (高級(jí)階段)采用 AOP 的方式,對(duì)代碼無(wú)侵入實(shí)現(xiàn)

除了統(tǒng)?的用戶登錄判斷之外,AOP 還可以實(shí)現(xiàn):

  • 統(tǒng)?日志記錄
  • 統(tǒng)一方法執(zhí)行時(shí)間統(tǒng)計(jì)
  • 統(tǒng)一的返回格式設(shè)置
  • 統(tǒng)一的異常處理
  • 事務(wù)的開啟和提交等
統(tǒng)一方法執(zhí)行時(shí)間統(tǒng)計(jì)項(xiàng)目監(jiān)控:監(jiān)控項(xiàng)目請(qǐng)求流量、監(jiān)控接口的響應(yīng)時(shí)間甚至每個(gè)方法的響應(yīng)時(shí)間
統(tǒng)一的返回格式設(shè)置

httpstatus: HTTP狀態(tài)碼

code: 業(yè)務(wù)狀態(tài)碼(后端響應(yīng)成功不代表業(yè)務(wù)辦理成功) 

msg: 業(yè)務(wù)處理失敗返回的信息

data: 

也就是說使用 AOP 可以擴(kuò)充多個(gè)對(duì)象的某個(gè)能力,所以 AOP 可以說是 OOP(Object Oriented Programming,面向?qū)ο缶幊蹋┑?strong>補(bǔ)充和完善。

2. AOP 的組成

2.1 切面(Aspect)

切面(Aspect)由切點(diǎn)(Pointcut)和通知(Advice)組成,它既包含了橫切邏輯的定義,也包括了連接點(diǎn)的定義。

切面是包含了:通知、切點(diǎn)和切面的類,相當(dāng)于 AOP 實(shí)現(xiàn)的某個(gè)功能的集合。

2.2 連接點(diǎn)(Join Point)

應(yīng)?執(zhí)行過程中能夠插入切面的?個(gè)點(diǎn),這個(gè)點(diǎn)可以是方法調(diào)用時(shí)、拋出異常時(shí),甚至修改字段 時(shí)。切面代碼可以利用這些點(diǎn)插入到應(yīng)用的正常流程之中,并添加新的行為。

連接點(diǎn)相當(dāng)于需要被增強(qiáng)的某個(gè) AOP 功能的所有方法。

2.3 切點(diǎn)(Pointcut)

Pointcut 是匹配 Join Point 的謂詞。 Pointcut 的作用就是提供?組規(guī)則(使用 AspectJ pointcut expression language 來描述)來匹配 Join Point,給滿足規(guī)則的 Join Point 添加 Advice。

切點(diǎn)相當(dāng)于保存了眾多連接點(diǎn)的一個(gè)集合(如果把切點(diǎn)看成一個(gè)表,而連接點(diǎn)就是表中?條?條 的數(shù)據(jù))。

2.4 通知(Advice)

切面也是有目標(biāo)的 ——它必須完成的工作。在 AOP 術(shù)語(yǔ)中,切面的工作被稱之為通知

Spring 切面類中,可以在方法上使用以下注解,會(huì)設(shè)置方法為通知方法,在滿足條件后會(huì)通知本 方法進(jìn)行調(diào)用:

  • 前置通知使用 @Before:通知方法會(huì)在目標(biāo)方法調(diào)用之前執(zhí)行。
  • 后置通知使用 @After:通知方法會(huì)在目標(biāo)方法返回或者拋出異常后調(diào)用。
  • 返回之后通知使用 @AfterReturning:通知方法會(huì)在目標(biāo)方法返回后調(diào)用。
  • 拋異常后通知使用 @AfterThrowing:通知方法會(huì)在目標(biāo)方法拋出異常后調(diào)用。
  • 環(huán)繞通知使用戶 @Around:通知包裹了被通知的方法,在被通知的方法通知之前和調(diào)用之后執(zhí)行自定義的行為。

切點(diǎn)相當(dāng)于要增強(qiáng)的方法。 

AOP 整個(gè)組成部分的概念如下圖所示,以多個(gè)頁(yè)面都要訪問用戶登錄權(quán)限為例:

既然說 AOP 是對(duì)一類事情的集中處理,那么我們就需要明確兩點(diǎn):

  •  一類事情:處理對(duì)象的一個(gè)范圍
  •  集中處理:處理的內(nèi)容是什么

我們通過生活中的一個(gè)例子來看一下:

比如,我們乘坐高鐵需要安檢

那么,我們需要處理的內(nèi)容就是安檢;處理的范圍就是需要乘坐高鐵的人。

此處乘坐高鐵需要安檢這件事情就是切面,處理的內(nèi)容安檢就是通知,處理的范圍乘坐高鐵的人就是切點(diǎn),具體有哪些人就是連接點(diǎn)。

切點(diǎn)是一個(gè)規(guī)則,事情的處理,最終作用在方法上。 

3. Spring AOP的實(shí)現(xiàn)

3.1 新建項(xiàng)目

3.2 添加 AOP 框架支持 

在 pom.xml 中添加如下配置:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

3.3 定義切面、切點(diǎn)和通知

 我們先定義 UserController 類:

@RequestMapping("/user")
@RestController
public class UserController {
    // 獲取用戶信息
    @RequestMapping("/getInfo")
    public String getInfo(){
        return "get info...";
    }
    // 注冊(cè)
    @RequestMapping("/reg")
    public String reg(){
        return "reg...";
    }
    // Login
    @RequestMapping("/login")
    public String login(){
        return "login...";
    }
}

運(yùn)行后,成功訪問: 

接下來,我們?cè)?UserController 類中定義切面和切點(diǎn): 

@Slf4j
@RequestMapping("/user")
@RestController
public class UserController {
    // 獲取用戶信息
    @RequestMapping("/getInfo")
    public String getInfo(){
        log.info("get info...");
        return "get info...";
    }
    // 注冊(cè)
    @RequestMapping("/reg")
    public String reg(){
        log.info("reg...");
        return "reg...";
    }
    // Login
    @RequestMapping("/login")
    public String login(){
        log.info("login...");
        return "login...";
    }
}

在 LoginAspect 類中使用 @Before 注解(通知方法會(huì)在目標(biāo)方法調(diào)用之前執(zhí)行): 

@Slf4j
@Component
@Aspect
public class LoginAspect {
    @Pointcut("execution(* com.example.demo.controller.UserController.* (..))")
    public void pointcut(){}
    @Before("pointcut()")
    public void doBefore(){
        log.info("do berore...");
    }
}

我們接著新建一個(gè) TestController 類:

@Slf4j
@RequestMapping("/test")
@RestController
public class TestController {
    @RequestMapping("/hi")
    public String hi(){
        log.info("hi~");
        return "hi~";
    }
}

可以看到運(yùn)行的結(jié)果中,并沒有在控制臺(tái)打印 @Before 中的內(nèi)容: 

那么為什么沒有執(zhí)行呢?

我們?cè)賮砜匆幌缕渌⒔猓?strong>@After(通知方法會(huì)在目標(biāo)方法返回或者拋出異常后調(diào)用

@Slf4j
@Component
@Aspect
public class LoginAspect {
    @Pointcut("execution(* com.example.demo.controller.UserController.* (..))")
    public void pointcut(){}
    @Before("pointcut()")
    public void doBefore(){
        log.info("do berore...");
    }
    @After("pointcut()")
    public void doAfter(){
        log.info("do after...");
    }
}

運(yùn)行結(jié)果如下:

@AfterReturning(通知方法會(huì)在目標(biāo)方法返回后調(diào)用)

@Slf4j
@Component
@Aspect
public class LoginAspect {
    @Pointcut("execution(* com.example.demo.controller.UserController.* (..))")
    public void pointcut(){}
    @Before("pointcut()")
    public void doBefore(){
        log.info("do berore...");
    }
    @After("pointcut()")
    public void doAfter(){
        log.info("do after...");
    }
    @AfterReturning("pointcut()")
    public void doAfterReturning(){
        log.info("do after returning...");
    }
}

運(yùn)行以上代碼后: 

可以看到 :@AfterReturning 在 @After 之前被調(diào)用。

@AfterThrowing(通知方法會(huì)在目標(biāo)方法拋出異常后調(diào)用)

我們首先在 UserController 類中加入異常:

@Slf4j
@RequestMapping("/user")
@RestController
public class UserController {
    // 獲取用戶信息
    @RequestMapping("/getInfo")
    public String getInfo(){
        log.info("get info...");
        return "get info...";
    }
    // 注冊(cè)
    @RequestMapping("/reg")
    public String reg(){
        log.info("reg...");
        int a = 10/0;
        return "reg...";
    }
    // Login
    @RequestMapping("/login")
    public String login(){
        log.info("login...");
        return "login...";
    }
}

添加 @AfterThrowing 注解:

@Slf4j
@Component
@Aspect
public class LoginAspect {
    @Pointcut("execution(* com.example.demo.controller.UserController.* (..))")
    public void pointcut(){}
    @Before("pointcut()")
    public void doBefore(){
        log.info("do berore...");
    }
    @After("pointcut()")
    public void doAfter(){
        log.info("do after...");
    }
    @AfterReturning("pointcut()")
    public void doAfterReturning(){
        log.info("do after returning...");
    }
    @AfterThrowing("pointcut()")
    public void doAfterThrowing(){
        log.info("do after throwing...");
    }
}

運(yùn)行后可以看到: 

當(dāng)正常返回時(shí),執(zhí)行 @AfterReturning 注解,當(dāng)出現(xiàn)異常時(shí),不會(huì)執(zhí)行 @AfterReturning 注解;

當(dāng)出現(xiàn)異常時(shí),才會(huì)執(zhí)行 @AfterThrowing 注解,當(dāng)正常返回時(shí),不會(huì)執(zhí)行 @AfterThrowing 注解。

@Around(通知包裹了被通知的方法,在被通知的方法通知之前和調(diào)用之后執(zhí)行自定義的行為):

添加  @Around 注解:

@Slf4j
@Component
@Aspect
public class LoginAspect {
    @Pointcut("execution(* com.example.demo.controller.UserController.* (..))")
    public void pointcut(){}
    @Before("pointcut()")
    public void doBefore(){
        log.info("do berore...");
    }
    @After("pointcut()")
    public void doAfter(){
        log.info("do after...");
    }
    @AfterReturning("pointcut()")
    public void doAfterReturning(){
        log.info("do after returning...");
    }
    @AfterThrowing("pointcut()")
    public void doAfterThrowing(){
        log.info("do after throwing...");
    }
    @Around("pointcut()")
    public void doAround(ProceedingJoinPoint joinPoint){
        log.info("環(huán)繞通知執(zhí)行之前...");
        try {
            joinPoint.proceed(); // 調(diào)用目標(biāo)方法
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
        log.info("環(huán)繞通知執(zhí)行之后...");
    }
}

運(yùn)行后界面顯示如下:

可以看到此時(shí)界面中不再有返回值,因此修改代碼如下:

@Around("pointcut()")
public Object doAround(ProceedingJoinPoint joinPoint){
    Object oj = null;
    log.info("環(huán)繞通知執(zhí)行之前...");
    try {
        oj = joinPoint.proceed(); // 調(diào)用目標(biāo)方法
    } catch (Throwable e) {
        throw new RuntimeException(e);
    }
    log.info("環(huán)繞通知執(zhí)行之后...");
    return oj;
}

此時(shí)可以看到成功返回并打印了值: 

我們?cè)賮砜匆幌逻@段代碼:

4. 切點(diǎn)表達(dá)式說明

AspectJ 支持三種通配符

  • * :匹配任意字符,只匹配一個(gè)元素(包,類,或方法,方法參數(shù))
  • .. :匹配任意字符,可以匹配多個(gè)元素 ,在表示類時(shí),必須和 * 聯(lián)合使用。
  • + :表示按照類型匹配指定類的所有類,必須跟在類名后面,如 com.cad.Car+ ,表示繼承該類的 所有子類包括本身

切點(diǎn)表達(dá)式由切點(diǎn)函數(shù)組成,其中 execution() 是最常用的切點(diǎn)函數(shù),用來匹配方法,語(yǔ)法為: 

execution(<修飾符><返回類型><包.類.方法(參數(shù))><異常>)

5. 練習(xí):使用 AOP 統(tǒng)計(jì) UserController 每個(gè)方法的執(zhí)行時(shí)間

@Slf4j
@Component
@Aspect
public class LoginAspect {
    @Pointcut("execution(* com.example.demo.controller.UserController.* (..))")
    public void pointcut(){}
    @Before("pointcut()")
    public void doBefore(){
        log.info("do berore...");
    }
    @After("pointcut()")
    public void doAfter(){
        log.info("do after...");
    }
    @AfterReturning("pointcut()")
    public void doAfterReturning(){
        log.info("do after returning...");
    }
    @AfterThrowing("pointcut()")
    public void doAfterThrowing(){
        log.info("do after throwing...");
    }
    @Around("pointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint){
        Object oj = null;
        log.info("環(huán)繞通知執(zhí)行之前...");
        try {
            oj = joinPoint.proceed(); // 調(diào)用目標(biāo)方法
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
        log.info("環(huán)繞通知執(zhí)行之后...");
        return oj;
    }
    /**
     *
     * @param joinPoint 使用 AOP 統(tǒng)計(jì) UserController 每個(gè)方法的執(zhí)行時(shí)間
     * @return
     */
    @Around("pointcut()")
    public Object doAroundCount(ProceedingJoinPoint joinPoint){
        Object oj = null;
        long start = System.currentTimeMillis();
        try {
            oj = joinPoint.proceed(); // 調(diào)用目標(biāo)方法
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
        log.info(joinPoint.getSignature().toString()+"耗時(shí):"+(System.currentTimeMillis()-start));
        return oj;
    }
}

可以看到不同的方法直接在 url 中進(jìn)行更改重新運(yùn)行界面即可獲得:

到此這篇關(guān)于Spring AOP 的組成和實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Spring AOP實(shí)現(xiàn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot+SseEmitter和Vue3+EventSource實(shí)現(xiàn)實(shí)時(shí)數(shù)據(jù)推送

    SpringBoot+SseEmitter和Vue3+EventSource實(shí)現(xiàn)實(shí)時(shí)數(shù)據(jù)推送

    本文主要介紹了SpringBoot+SseEmitter和Vue3+EventSource實(shí)現(xiàn)實(shí)時(shí)數(shù)據(jù)推送,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-03-03
  • 老生常談比較排序之歸并排序(遞歸)

    老生常談比較排序之歸并排序(遞歸)

    下面小編就為大家?guī)硪黄仙U劚容^排序之歸并排序(遞歸)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-06-06
  • 解決idea出現(xiàn)的java.lang.OutOfMemoryError:?Java?heap?space的問題

    解決idea出現(xiàn)的java.lang.OutOfMemoryError:?Java?heap?space的問題

    我們?cè)谑褂胕dea的時(shí)候經(jīng)常會(huì)遇到一些問題,本文介紹了如何解決idea出現(xiàn)的java.lang.OutOfMemoryError:?Java?heap?space的問題,文中有相關(guān)的圖文示例,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • java實(shí)現(xiàn)簡(jiǎn)單控制臺(tái)通訊錄

    java實(shí)現(xiàn)簡(jiǎn)單控制臺(tái)通訊錄

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡(jiǎn)單控制臺(tái)通訊錄,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-02-02
  • Java實(shí)現(xiàn)的求逆矩陣算法示例

    Java實(shí)現(xiàn)的求逆矩陣算法示例

    這篇文章主要介紹了Java實(shí)現(xiàn)的求逆矩陣算法,涉及java基于數(shù)組的矩陣遍歷與運(yùn)算相關(guān)操作技巧,需要的朋友可以參考下
    2017-09-09
  • Mybatis?Plus插入數(shù)據(jù)后獲取新數(shù)據(jù)id值的踩坑記錄

    Mybatis?Plus插入數(shù)據(jù)后獲取新數(shù)據(jù)id值的踩坑記錄

    在某些情況下,需要在執(zhí)行新增后,需要獲取到新增行的id,這篇文章主要給大家介紹了關(guān)于Mybatis?Plus插入數(shù)據(jù)后獲取新數(shù)據(jù)id值的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-08-08
  • 使用SpringBoot+nmap4j獲取端口信息的代碼詳解

    使用SpringBoot+nmap4j獲取端口信息的代碼詳解

    這篇文章主要介紹了使用 SpringBoot + nmap4j 獲取端口信息,包括需求背景、nmap4j 的相關(guān)介紹、代碼說明(含測(cè)試代碼、改造后的代碼及參數(shù)說明),還提到了文件讀取方式和依賴引入方式,最終請(qǐng)求能獲取到數(shù)據(jù),需要的朋友可以參考下
    2025-01-01
  • springboot驗(yàn)證碼的生成與驗(yàn)證的兩種方法

    springboot驗(yàn)證碼的生成與驗(yàn)證的兩種方法

    本文主要介紹了springboot驗(yàn)證碼的生成與驗(yàn)證的兩種方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • Spring源碼解析后置處理器梳理總結(jié)

    Spring源碼解析后置處理器梳理總結(jié)

    這篇文章主要介紹了Spring源碼解析后置處理器梳理總結(jié),在前面幾篇文章中梳理了Spring中bean的創(chuàng)建過程,在這個(gè)過程中各式各樣的后置處理器發(fā)揮了不同的作用,可以說后置處理器貫穿了bean的實(shí)例化以及初始化過程
    2022-07-07
  • SpringCloud Bus消息總線的實(shí)現(xiàn)

    SpringCloud Bus消息總線的實(shí)現(xiàn)

    消息總線是一種通信工具,可以在機(jī)器之間互相傳輸消息、文件等,這篇文章主要介紹了SpringCloud Bus消息總線的實(shí)現(xiàn),Spring cloud bus 通過輕量消息代理連接各個(gè)分布的節(jié)點(diǎn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-05-05

最新評(píng)論