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

Spring AOP快速入門(mén)及開(kāi)發(fā)步驟

 更新時(shí)間:2024年10月22日 14:48:03   作者:gobeyye  
Spring AOP(面向切面編程)核心概念包括切面(Aspect)、連接點(diǎn)(JoinPoint)、切點(diǎn)(Pointcut)、通知(Advice)等,通過(guò)在不改變?cè)a的情況下,對(duì)方法進(jìn)行增強(qiáng),實(shí)現(xiàn)了代碼的解耦和功能擴(kuò)展,本文帶來(lái)大家掌握Spring 中 AOP 的開(kāi)發(fā)步驟,感興趣的朋友一起看看吧

Spring 框架有兩大核心 IoC,AOP。在前面我們已經(jīng)學(xué)習(xí)過(guò)了 IoC 的相關(guān)知識(shí),今天就讓我們開(kāi)始 AOP 的學(xué)習(xí)。

一、AOP 概述

Aspect Oriented Programming(面向切面編程)。

切面就是指某一類(lèi)特定問(wèn)題,所以 AOP 也可以理解為面向特定方法編程。

**AOP 是一種思想,是對(duì)某一類(lèi)事情的集中處理。**Spring AOP 是其中的一種實(shí)現(xiàn)方式。

AOP 的作用:在程序運(yùn)行期間,在不修改源代碼的基礎(chǔ)上,對(duì)已有方法進(jìn)行增強(qiáng)(無(wú)侵入性:解耦)。

二、Spring AOP 快速入門(mén)

我們先通過(guò)下面的程序體驗(yàn)下 AOP 的開(kāi)發(fā),并掌握 Spring 中 AOP 的開(kāi)發(fā)步驟。

2.1 引入 AOP 依賴(lài):

在 pom.xml 文件中添加配置:

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

2.2 編寫(xiě) AOP 程序:

@Aspect
@Slf4j
@Component
public class TestAspect {
    @Around("execution(* com.example.demo.controller.*.*(..))")
    public Object demo(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("方法執(zhí)行前執(zhí)行");
        Object result = joinPoint.proceed();
        log.info("方法執(zhí)行后執(zhí)行");
        return result;
    }
}

controller 類(lèi):

@RequestMapping("/test")
@RestController
@Slf4j
public class TestController {
    @RequestMapping("/t1")
    public void test1(){
        log.info("我是 test1");
    }
}

調(diào)用 controller 中的 test1 方法。

結(jié)果如下:

對(duì)程序進(jìn)行簡(jiǎn)單的講解:

  • @Aspect:標(biāo)識(shí)這是一個(gè)切面類(lèi)。
  • @Around:環(huán)繞通知,在目標(biāo)方法的前后都會(huì)被執(zhí)行。后面的表達(dá)式表示對(duì)哪些方法進(jìn)行增強(qiáng)。
  • ProceedingJoinPoint.proceed()讓原始方法執(zhí)行。

整個(gè)代碼劃分為三部分。

通過(guò)上面的程序,我們也可以感受到 AOP 面向切面編程的一些優(yōu)勢(shì):

  • 代碼無(wú)侵入:不修改原始的業(yè)務(wù)方法,就可以對(duì)原始的業(yè)務(wù)方法進(jìn)行了功能的增強(qiáng)或者是功能的改變。
  • 減少了重復(fù)代碼。
  • 提高開(kāi)發(fā)效率。
  • 維護(hù)方便。

三、Spring AOP 詳解

3.1 Spring AOP 核心概念:

3.1.1 切點(diǎn)(Pointcut):

切點(diǎn)(Pointcut),也稱(chēng)之為"切入點(diǎn)"。

Pointcut 的作用就是提供一組規(guī)則(使用 AspectJ pointcut expression language 來(lái)描述),告訴程序?qū)δ男┓椒▉?lái)進(jìn)行功能增強(qiáng)。

上面的表達(dá)式 execution(* com.example.demo.controller..(…)) 就是切點(diǎn)表達(dá)式。

3.1.2 連接點(diǎn)(Join Point):

滿(mǎn)足切點(diǎn)表達(dá)式規(guī)則的方法,就是連接點(diǎn)。也就是可以被 AOP 控制的方法。

切點(diǎn)和連接點(diǎn)的關(guān)系:

連接點(diǎn)是滿(mǎn)足切點(diǎn)表達(dá)式的元素。切點(diǎn)可以看做是保存了眾多連接點(diǎn)的一個(gè)集合。

3.1.3 通知(Advice):

通知就是具體要做的工作,指哪些重復(fù)的邏輯,也就是共性功能(最終體現(xiàn)為一個(gè)方法)。

在 AOP 面向切面編程當(dāng)中,我們把這部分重復(fù)的代碼邏輯抽取出來(lái)單獨(dú)定義,這部分代碼就是通知的內(nèi)容。

3.1.4 切面(Aspect):

切面(Aspect)= 切點(diǎn)(Pointcut)+ 通知(Advice)。

通過(guò)切面就能夠描述當(dāng)前 AOP 程序需要針對(duì)于哪些方法,在什么時(shí)候執(zhí)行什么樣的操作。

切面既包含了通知邏輯的定義,也包括了連接點(diǎn)的定義。

切面所在的類(lèi),我們一般稱(chēng)為切面類(lèi)(被 @Aspect 注解標(biāo)識(shí)的類(lèi))。

3.2 通知類(lèi)型:

上面我們講了什么是通知,接下來(lái)學(xué)習(xí)通知的類(lèi)型。@Around 就是其中一種通知類(lèi)型,表示環(huán)繞通知。Spring 中 AOP 的通知類(lèi)型有以下幾種:

  • @Around:環(huán)繞通知,此注解標(biāo)注的通知方法在目標(biāo)方法前后都被執(zhí)行。
  • @Before:前置通知,此注解標(biāo)注的通知方法在目標(biāo)方法前被執(zhí)行。
  • @After:后置通知,此注解標(biāo)注的通知方法在目標(biāo)方法后被執(zhí)行,無(wú)論是否有異常都會(huì)執(zhí)行。
  • @AfterReturning:返回后通知,此注解標(biāo)注的通知方法在目標(biāo)方法返回后被執(zhí)行,有異常不會(huì)執(zhí)行。
  • @AfterThrowing:異常后通知,此注解標(biāo)注的通知方法發(fā)生異常后執(zhí)行。

沒(méi)有異常的運(yùn)行順序:

程序正常運(yùn)行的情況下,@AfterThrowing 標(biāo)識(shí)的通知方法不會(huì)執(zhí)行。

出現(xiàn)異常的運(yùn)行順序:

@AfterReturning 標(biāo)識(shí)的通知方法不會(huì)執(zhí)行,@AfterThrowing 標(biāo)識(shí)的通知方法執(zhí)行了。

@Around 環(huán)繞通知中原始方法調(diào)用時(shí)有異常,通知中的環(huán)繞后的代碼邏輯也不會(huì)再執(zhí)行了(因?yàn)樵挤椒ㄕ{(diào)用出異常了)。

注意:

  • @Around 環(huán)繞通知需要調(diào)用 ProceedingJoinPoint.proceed() 來(lái)讓原始方法執(zhí)行,其他通知不需要考慮目標(biāo)方法執(zhí)行。
  • @Around 環(huán)繞通知方法的返回值,必須指定為 Object,來(lái)接收原始方法的返回值,否則原始方法執(zhí)行完畢,是獲取不到返回值的。
  • 一個(gè)切面類(lèi)可以有多個(gè)切點(diǎn)。

3.3 @PointCut:

Spring 提供了 @PointCut 注解,把公共的切點(diǎn)表達(dá)式提取出來(lái),需要用到時(shí)引用該切入點(diǎn)表達(dá)式即可,便于后續(xù)代碼的維護(hù)。

@Aspect
@Slf4j
@Component
public class TestAspect {
    @Pointcut("execution(* com.example.demo.controller.*.*(..))")
    public void pt(){}
    @Around("pt()")
    public Object demo(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("方法執(zhí)行前執(zhí)行");
        Object result = joinPoint.proceed();
        log.info("方法執(zhí)行后執(zhí)行");
        return result;
    }
}

當(dāng)切點(diǎn)定義使用 private 修飾時(shí),僅能在當(dāng)前切面類(lèi)中使用,當(dāng)其他切面類(lèi)也要使用當(dāng)前切點(diǎn)定義時(shí),就需要把 private 改為 public。引用方式為:全限定類(lèi)名.方法名()。

@Slf4j
@Component
@Aspect
public class TestAspect2 {
    @Before("com.example.demo.aspect.TestAspect.pt()")
    public void doBefore() {
        log.info("執(zhí)? TestAspect2 -> Before ?法");
    }
}

3.4 切面優(yōu)先級(jí) @Order:

當(dāng)我們?cè)谝粋€(gè)項(xiàng)目中,定義了多個(gè)切面類(lèi)時(shí),并且這些切面類(lèi)的多個(gè)切入點(diǎn)都匹配到了同一個(gè)目標(biāo)方法。當(dāng)目標(biāo)方法運(yùn)行的時(shí)候,這些切面類(lèi)中的通知方法都會(huì)執(zhí)行,那么這幾個(gè)通知方法的執(zhí)行順序是什么樣的呢?

我們通過(guò)程序來(lái)進(jìn)行驗(yàn)證。

@Slf4j
@Component
@Aspect
public class TestAspect2 {
    @Pointcut("execution(* com.example.demo.controller.*.*(..))")
    private void pt(){}
    //前置通知
    @Before("pt()")
    public void doBefore() {
        log.info("執(zhí)行 TestAspect2 -> Before 方法");
    }
    //后置通知
    @After("pt()")
    public void doAfter() {
        log.info("執(zhí)行 TestAspect2 -> After 方法");
    }
}
@Aspect
@Component
@Slf4j
public class TestAspect3 {
    @Pointcut("execution(* com.example.demo.controller.*.*(..))")
    private void pt(){}
    //前置通知
    @Before("pt()")
    public void doBefore() {
        log.info("執(zhí)行 TestAspect3 -> Before 方法");
    }
    //后置通知
    @After("pt()")
    public void doAfter() {
        log.info("執(zhí)行 TestAspect3 -> After 方法");
    }
}
@Aspect
@Component
@Slf4j
public class TestAspect4 {
    @Pointcut("execution(* com.example.demo.controller.*.*(..))")
    private void pt(){}
    //前置通知
    @Before("pt()")
    public void doBefore() {
        log.info("執(zhí)行 TestAspect4 -> Before 方法");
    }
    //后置通知
    @After("pt()")
    public void doAfter() {
        log.info("執(zhí)行 TestAspect4 -> After 方法");
    }
}

運(yùn)行上面程序:

通過(guò)上述程序的運(yùn)行結(jié)果,可以看出:

存在多個(gè)切面類(lèi)時(shí),默認(rèn)按照切面類(lèi)的類(lèi)名字母排序:

  • @Before 通知:字母排名靠前的先執(zhí)行。
  • @After 通知:字母排名靠前的后執(zhí)行。

但這種方式不方便管理,我們的類(lèi)名更多還是具備一定含義的。

Spring 給我們提供了一個(gè)新的注解,來(lái)控制這些切面通知的執(zhí)行順序:@Order。

@Slf4j
@Component
@Aspect
@Order(10)
public class TestAspect2 {
    //代碼省略
}
@Aspect
@Component
@Slf4j
@Order(5)
public class TestAspect3 {
    //代碼省略
}
@Aspect
@Component
@Slf4j
@Order(1)
public class TestAspect4 {
    //代碼省略
}

運(yùn)行程序:

通過(guò)上述程序的運(yùn)行結(jié)果,得出結(jié)論:

@Order 注解標(biāo)識(shí)的切面類(lèi),執(zhí)行順序如下:

  • @Before 通知:數(shù)字越小先執(zhí)行。
  • @After 通知:數(shù)字越大先執(zhí)行。

@Order 的執(zhí)行順序可以抽象成下面這張圖:

3.5 切點(diǎn)表達(dá)式:

上面的代碼中,我們一直在使用切點(diǎn)表達(dá)式來(lái)描述切點(diǎn)。下面我們來(lái)介紹一下切點(diǎn)表達(dá)式的語(yǔ)法。

切點(diǎn)表達(dá)式常見(jiàn)有兩種表達(dá)方式:

  • execution:根據(jù)方法的簽名來(lái)匹配。
  • @annotation:根據(jù)注解匹配。

3.5.1 execution 表達(dá)式:

execution() 是最常用的切點(diǎn)表達(dá)式,用來(lái)匹配方法,語(yǔ)法為:

execution (<訪問(wèn)修飾符> <返回類(lèi)型> <包名.類(lèi)名.方法(方法參數(shù))> <異常>)

其中:訪問(wèn)修飾符和異常可以省略。

切點(diǎn)表達(dá)式支持通配符表達(dá):

  • * :匹配任意字符,只匹配一個(gè)元素(返回類(lèi)型,包,類(lèi)名,方法或者方法參數(shù))。
  • 包名使用 * 表示任意包(一層包使用一個(gè) * )。類(lèi)名使用 * 表示任意類(lèi)。返回值使用 * 表示任意返回值類(lèi)型。
  • 方法名使用 * 表示任意方法(參數(shù)可能有限制)。
  • 參數(shù)使用 * 表示一個(gè)任意類(lèi)型的參數(shù)。

..:匹配多個(gè)連續(xù)的任意符號(hào),可以通配任意層級(jí)的包,或任意類(lèi)型,任意個(gè)數(shù)的參數(shù)。

  • 使用..配置包名,標(biāo)識(shí)此包以及此包下的所有子包。
  • 可以使用..配置參數(shù),任意個(gè)任意類(lèi)型的參數(shù)。

3.5.2 @annotation:

execution 表達(dá)式更適用有規(guī)則的,如果我們要匹配多個(gè)無(wú)規(guī)則的方法呢,比如:TestController 中的 t1() 和 UserController 中的 u1() 這兩個(gè)方法。這個(gè)時(shí)候我們使用 execution 這種切點(diǎn)表達(dá)式來(lái)描述就不是很方便了。我們可以借助自定義注解的方式以及另一種切點(diǎn)表達(dá)式 @annotation 來(lái)描述這一類(lèi)的切點(diǎn)。

實(shí)現(xiàn)步驟:

  • 編寫(xiě)自定義注解。
  • 使用 @annotation 表達(dá)式來(lái)描述切點(diǎn)。
  • 在方法上添加自定義注解。

創(chuàng)建一個(gè)注解類(lèi)(和創(chuàng)建 Class 文件一樣的流程,選擇 Annotation 就可以了)

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAspect {
}

使用 @annotation 表達(dá)式來(lái)描述切點(diǎn)。

@Component
@Slf4j
@Aspect
public class MyAspectDemo {
    @Before("@annotation(com.example.demo.aspect.MyAspect)")
    public void doBefore(){
        log.info("我是 MyAspectDemo");
    }
}

在方法上添加自定義注解。

@RequestMapping("/test")
@RestController
@Slf4j
public class TestController {
    @MyAspect
    @RequestMapping("/t1")
    public void test1(){
        log.info("我是 test1");
    }
}

運(yùn)行程序,訪問(wèn) test1 方法。

3.6 Spring AOP 的實(shí)現(xiàn)方式(常見(jiàn)面試題):

  • 基于注解 @Aspect。
  • 基于自定義注解(@annotation)。
  • 基于 Spring API(通過(guò) xml 配置的方式,自從 SpringBoot 廣泛使用之后,這種方法幾乎看不到了)。
  • 基于代理來(lái)實(shí)現(xiàn)(更加久遠(yuǎn)的一種實(shí)現(xiàn)方式,寫(xiě)法笨重,不建議使用)。

四、代理模式

Spring AOP 是基于動(dòng)態(tài)代理來(lái)實(shí)現(xiàn) AOP 的。

代理模式,也叫委托模式。

定義:

為其他對(duì)象提供一種代理,以控制對(duì)這個(gè)對(duì)象的訪問(wèn)。它的作用就是通過(guò)提供一個(gè)代理類(lèi),讓我們?cè)谡{(diào)用目標(biāo)方法的時(shí)候,不再是直接對(duì)目標(biāo)方法進(jìn)行調(diào)用,而是通過(guò)代理類(lèi)間接調(diào)用。

代理模式可以在不修改被代理對(duì)象的基礎(chǔ)上,通過(guò)擴(kuò)展代理類(lèi),進(jìn)行一些功能的附加與增強(qiáng)。

根據(jù)代理的創(chuàng)建時(shí)期,代理模式分為靜態(tài)代理動(dòng)態(tài)代理。

  • 靜態(tài)代理:由程序員創(chuàng)建代理類(lèi)或特定工具自動(dòng)生成源代碼再對(duì)其編譯,在程序運(yùn)行前代理類(lèi)的 .class 文件就已經(jīng)存在了。
  • 動(dòng)態(tài)代理:在程序運(yùn)行時(shí),運(yùn)用反射機(jī)制動(dòng)態(tài)創(chuàng)建而成。

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

相關(guān)文章

  • Java替換int數(shù)組中重復(fù)數(shù)據(jù)的方法示例

    Java替換int數(shù)組中重復(fù)數(shù)據(jù)的方法示例

    這篇文章主要介紹了Java替換int數(shù)組中重復(fù)數(shù)據(jù)的方法,涉及java針對(duì)數(shù)組的遍歷、轉(zhuǎn)換、判斷等相關(guān)操作技巧,需要的朋友可以參考下
    2017-06-06
  • java判斷請(qǐng)求是來(lái)自PC端還是手機(jī)端小技巧

    java判斷請(qǐng)求是來(lái)自PC端還是手機(jī)端小技巧

    這篇文章主要為大家介紹了java判斷請(qǐng)求是來(lái)自PC端還是手機(jī)端小技巧,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-06-06
  • Java中的@PostConstruct注解的使用

    Java中的@PostConstruct注解的使用

    本文主要介紹了Java中的@PostConstruct注解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • java實(shí)現(xiàn)將文件上傳到ftp服務(wù)器的方法

    java實(shí)現(xiàn)將文件上傳到ftp服務(wù)器的方法

    這篇文章主要介紹了java實(shí)現(xiàn)將文件上傳到ftp服務(wù)器的方法,結(jié)合實(shí)例形式分析了基于java實(shí)現(xiàn)的ftp文件傳輸類(lèi)定義與使用方法,需要的朋友可以參考下
    2016-08-08
  • Spring 多線程下注入bean問(wèn)題詳解

    Spring 多線程下注入bean問(wèn)題詳解

    本篇文章主要介紹了Spring 多線程下注入bean問(wèn)題詳解,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-10-10
  • Springboot整合hibernate validator 全局異常處理步驟詳解

    Springboot整合hibernate validator 全局異常處理步驟詳解

    本文分步驟給大家介紹Springboot整合hibernate validator 全局異常處理,補(bǔ)呢文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2024-01-01
  • Java中Stream?API的使用示例詳解

    Java中Stream?API的使用示例詳解

    Java?在?Java?8?中提供了一個(gè)新的附加包,稱(chēng)為?java.util.stream,該包由類(lèi)、接口和枚舉組成,允許對(duì)元素進(jìn)行函數(shù)式操作,?本文主要介紹了Java中Stream?API的具體使用,感興趣的小伙伴可以了解下
    2023-11-11
  • App登陸java后臺(tái)處理和用戶(hù)權(quán)限驗(yàn)證

    App登陸java后臺(tái)處理和用戶(hù)權(quán)限驗(yàn)證

    這篇文章主要為大家詳細(xì)介紹了App登陸java后臺(tái)處理和用戶(hù)權(quán)限驗(yàn)證,感興趣的朋友可以參考一下
    2016-06-06
  • Idea中springboot項(xiàng)目的熱部署無(wú)法生效問(wèn)題解決

    Idea中springboot項(xiàng)目的熱部署無(wú)法生效問(wèn)題解決

    本文主要介紹了Idea中springboot項(xiàng)目的熱部署無(wú)法生效問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-10-10
  • Java 入門(mén)圖形用戶(hù)界面設(shè)計(jì)之列表框JList

    Java 入門(mén)圖形用戶(hù)界面設(shè)計(jì)之列表框JList

    圖形界面(簡(jiǎn)稱(chēng)GUI)是指采用圖形方式顯示的計(jì)算機(jī)操作用戶(hù)界面。與早期計(jì)算機(jī)使用的命令行界面相比,圖形界面對(duì)于用戶(hù)來(lái)說(shuō)在視覺(jué)上更易于接受,本篇精講Java語(yǔ)言中關(guān)于圖形用戶(hù)界面的列表框JList
    2022-02-02

最新評(píng)論