最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時(shí)間:2023年07月31日 10:16:53   作者:小魚(yú)的學(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ì)某一類(lèi)事情的集中處理。

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

開(kāi)發(fā)三階段

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

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

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

  • 統(tǒng)?日志記錄
  • 統(tǒng)一方法執(zhí)行時(shí)間統(tǒng)計(jì)
  • 統(tǒng)一的返回格式設(shè)置
  • 統(tǒng)一的異常處理
  • 事務(wù)的開(kāi)啟和提交等
統(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: 

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

2. AOP 的組成

2.1 切面(Aspect)

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

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

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

應(yīng)?執(zhí)行過(guò)程中能夠插入切面的?個(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 來(lái)描述)來(lái)匹配 Join Point,給滿(mǎn)足規(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ǔ)中,切面的工作被稱(chēng)之為通知

Spring 切面類(lèi)中,可以在方法上使用以下注解,會(huì)設(shè)置方法為通知方法,在滿(mǎn)足條件后會(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)繞通知使用戶(hù) @Around:通知包裹了被通知的方法,在被通知的方法通知之前和調(diào)用之后執(zhí)行自定義的行為。

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

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

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

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

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

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

那么,我們需要處理的內(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 類(lèi):

@RequestMapping("/user")
@RestController
public class UserController {
    // 獲取用戶(hù)信息
    @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)行后,成功訪問(wèn): 

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

@Slf4j
@RequestMapping("/user")
@RestController
public class UserController {
    // 獲取用戶(hù)信息
    @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 類(lèi)中使用 @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 類(lèi):

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

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

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

我們?cè)賮?lái)看一下其他注解,@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 類(lèi)中加入異常:

@Slf4j
@RequestMapping("/user")
@RestController
public class UserController {
    // 獲取用戶(hù)信息
    @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è)賮?lái)看一下這段代碼:

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

AspectJ 支持三種通配符

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

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

execution(<修飾符><返回類(lèi)型><包.類(lèi).方法(參數(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)文章

  • Java中ArrayList刪除的常用操作及方法

    Java中ArrayList刪除的常用操作及方法

    ArrayList是最常用的一種java集合,在開(kāi)發(fā)中我們常常需要從ArrayList中刪除特定元素,本文主要介紹了Java中ArrayList刪除的常用操作及方法,感興趣的可以了解一下
    2023-11-11
  • IntelliJ IDEA 配置與插件全部遷移到其他盤(pán)徹底釋放C盤(pán)空間

    IntelliJ IDEA 配置與插件全部遷移到其他盤(pán)徹底釋放C盤(pán)空間

    這篇文章給大家介紹IntelliJ IDEA 配置與插件全部遷移到其他盤(pán)徹底釋放C盤(pán)空間,本文將詳細(xì)解釋這一問(wèn)題的根源,并提供兩種實(shí)用的遷移方案,幫你把 IDEA 的所有用戶(hù)數(shù)據(jù)安全地移到其他盤(pán),徹底釋放 C 盤(pán)空間,感興趣的朋友跟隨小編一起看看吧
    2026-03-03
  • SpringBoot解析JSON數(shù)據(jù)的三種方案

    SpringBoot解析JSON數(shù)據(jù)的三種方案

    JSON(JavaScript Object Notation) 是一種輕量級(jí)的數(shù)據(jù)交換格式,易于人閱讀和編寫(xiě),同時(shí)也易于機(jī)器解析和生成,本文給大家介紹了SpringBoot解析JSON數(shù)據(jù)的三種方案,需要的朋友可以參考下
    2024-03-03
  • Java對(duì)int[]數(shù)組做新增刪除去重操作代碼

    Java對(duì)int[]數(shù)組做新增刪除去重操作代碼

    這篇文章主要介紹了Java里面對(duì)int[]數(shù)組做新增刪除去重實(shí)現(xiàn),這里記錄下使用int[]數(shù)組對(duì)數(shù)組進(jìn)行新增刪除去重等操作,用來(lái)更加了解java里面的集合類(lèi)思想,需要的朋友可以參考下
    2023-10-10
  • Spring Boot 驗(yàn)證碼框架 CAPTCHA詳解

    Spring Boot 驗(yàn)證碼框架 CAPTCHA詳解

    這篇文章主要介紹了Spring Boot 驗(yàn)證碼框架 CAPTCHA詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-03-03
  • Java 中的 CompletableFuture如何讓異步編程變得簡(jiǎn)單

    Java 中的 CompletableFuture如何讓異步編程變得簡(jiǎn)單

    CompletableFuture是Java 8引入的異步編程神器,它提供了強(qiáng)大的鏈?zhǔn)讲僮?、任?wù)組合和異常處理功能,使得異步編程變得簡(jiǎn)單優(yōu)雅,本文給大家介紹Java中的CompletableFuture如何讓異步編程變得簡(jiǎn)單,感興趣的朋友跟隨小編一起看看吧
    2025-12-12
  • java枚舉enum和Enum類(lèi)的使用

    java枚舉enum和Enum類(lèi)的使用

    本文主要介紹了java枚舉enum和Enum類(lèi)的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • Java web網(wǎng)站訪問(wèn)量的統(tǒng)計(jì)

    Java web網(wǎng)站訪問(wèn)量的統(tǒng)計(jì)

    這篇文章主要為大家詳細(xì)介紹了Java web網(wǎng)站訪問(wèn)量的統(tǒng)計(jì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-01-01
  • Java使用TCP套接字實(shí)現(xiàn)多人聊天功能詳解

    Java使用TCP套接字實(shí)現(xiàn)多人聊天功能詳解

    這篇文章主要介紹了Java使用TCP套接字實(shí)現(xiàn)多人聊天功能,結(jié)合實(shí)例形式詳細(xì)分析了java使用socket通信實(shí)現(xiàn)tcp協(xié)議下的聊天功能客戶(hù)端與服務(wù)器端相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2019-10-10
  • Java中的可重入鎖ReentrantLock簡(jiǎn)析

    Java中的可重入鎖ReentrantLock簡(jiǎn)析

    這篇文章主要介紹了Java中的可重入鎖ReentrantLock簡(jiǎn)析,可重入是指同一個(gè)線程如果首次獲得了這把鎖,那么因?yàn)樗沁@把鎖的擁有者,因此有權(quán)利再次獲取這把鎖如果是不可重入鎖,那么第二次獲得鎖時(shí),自己也會(huì)被鎖擋住,需要的朋友可以參考下
    2023-12-12

最新評(píng)論

文登市| 鹤山市| 灵武市| 曲阜市| 尤溪县| 耿马| 广宗县| 青河县| 运城市| 石河子市| 郁南县| 万荣县| 湘潭市| 鲜城| 来宾市| 盖州市| 石阡县| 合肥市| 资中县| 秀山| 图片| 信丰县| 沅江市| 丹寨县| 崇礼县| 临江市| 浮梁县| 宜兰县| 论坛| 法库县| 会昌县| 海阳市| 郴州市| 临湘市| 阿坝县| 吉木乃县| 河南省| 无棣县| 天台县| 五大连池市| 新野县|