日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學(xué)無(wú)先后,達(dá)者為師

網(wǎng)站首頁(yè) 編程語(yǔ)言 正文

Spring AOP講解及實(shí)例

作者:今天你學(xué)Java了嗎 更新時(shí)間: 2023-07-04 編程語(yǔ)言

Aop面向切面編程

文章目錄

  • Aop面向切面編程
    • 什么是AOP
    • AOP術(shù)語(yǔ)
    • Spring AOP 的使用
      • 導(dǎo)入依賴
      • 編寫(xiě)切面類
      • 切面定義語(yǔ)法
      • 小細(xì)節(jié)
      • 輸出日志成功

什么是AOP

AOP:(Aspect Oriented Programming)面向切面編程,和OOP(Object Oriented Programming)面向?qū)ο缶幊桃粯樱彩怯?jì)算機(jī)開(kāi)發(fā)的一種程序設(shè)計(jì)思想,與OOP將程序中的每個(gè)環(huán)節(jié)對(duì)象化,如實(shí)體類等相比,面向切面就是在不改變程序現(xiàn)有代碼的前提下,可以設(shè)置某方法運(yùn)行前或運(yùn)行后新增額外代碼的操作,減少對(duì)代碼的入侵。包括過(guò)濾器、攔截器都是一種AOP的思想,只不過(guò)Spring AOP是Spring給的,(注:AOP并不是Spring框架獨(dú)有的,而是從AspectJ框架中借鑒而來(lái))過(guò)濾器是Java給的,攔截器是SpringMVC給的。

對(duì)AOP官方文檔翻譯:

AOP目標(biāo)是將橫切關(guān)注點(diǎn)與業(yè)務(wù)主體進(jìn)行進(jìn)一步分離,以提高程序代碼的模塊化程度。通過(guò)在現(xiàn)有代碼基礎(chǔ)上增加額外的通知Advice)機(jī)制,能夠?qū)Ρ宦暶鳛椤?strong>切點(diǎn)“(Pointcut)的代碼塊進(jìn)行統(tǒng)一管理與擴(kuò)展

AOP術(shù)語(yǔ)

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-EqfMpLBJ-1688283328056)(../AppData/Roaming/Typora/typora-user-images/image-20230702134627970.png)]

  • 切面(Aspect):是一個(gè)可以加入額外代碼運(yùn)行的特定位置,一般指方法之間的調(diào)用,可以在不修改原代碼的情況下,添加新的代碼,對(duì)現(xiàn)有代碼進(jìn)行升級(jí)維護(hù)和管理

  • 織入(Weaving):選定一個(gè)切面,利用動(dòng)態(tài)代理技術(shù),為原有的方法的持有者生成動(dòng)態(tài)對(duì)象,然后將它和切面關(guān)聯(lián),在運(yùn)行原有方法時(shí),就會(huì)按織入之后的流程運(yùn)行了

    • 動(dòng)態(tài)代理:

      [外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-wOw6mwgq-1688283328057)(../AppData/Roaming/Typora/typora-user-images/image-20230702135722664.png)]

  • 目標(biāo)對(duì)象(Target):需要被加強(qiáng)的業(yè)務(wù)對(duì)象

  • 代理類(Proxy) 一個(gè)類被AOP織入通知后,就產(chǎn)生了一個(gè)代理類。

  • 切點(diǎn)(PointCut):每個(gè)程序的連接點(diǎn)有多個(gè),如何定位到某個(gè)感興趣的連接點(diǎn),就需要通過(guò)切點(diǎn)來(lái)定位。

  • 連接點(diǎn)(Joinpoint):程序執(zhí)行的某個(gè)特定位置,如某個(gè)方法調(diào)用前,調(diào)用后,方法拋出異常后,這些代碼中的特定點(diǎn)稱為連接點(diǎn)。簡(jiǎn)單來(lái)說(shuō),就是在哪加入你的通知

    • 連接點(diǎn)表示具體要攔截的方法,切點(diǎn)是定義一個(gè)范圍,而連接點(diǎn)是具體到某個(gè)方法
  • 通知(Advice):原意為增強(qiáng),是織入到目標(biāo)類連接點(diǎn)上的一段程序代碼。

    分以下幾種:

    • 前置通知(before):執(zhí)行業(yè)務(wù)代碼前做些操作,比如獲取連接對(duì)象
    • 后置通知(after):在執(zhí)行業(yè)務(wù)代碼后做些操作,無(wú)論是否發(fā)生異常,它都會(huì)執(zhí)行,比如關(guān)閉連接對(duì)象
    • 異常通知(afterThrowing):在執(zhí)行業(yè)務(wù)代碼后出現(xiàn)異常,需要做的操作,比如回滾事務(wù)
    • 返回通知(afterReturning):在執(zhí)行業(yè)務(wù)代碼后無(wú)異常,會(huì)執(zhí)行的操作
    • 環(huán)繞通知(around):

Spring AOP 的使用

導(dǎo)入依賴

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

編寫(xiě)切面類

@Slf4j
@Aspect    //切面
@Component
public class LogAspect {

    private HashMap<String, Object> map = new HashMap<>();
    private final String filePath = "logFile.txt";


    //1.定義切面
    /**
     * 此步驟單純定義切面,就是指定一個(gè)方法
     * 可以在這個(gè)方法運(yùn)行前或運(yùn)行后等位置織入額外代碼
     *
     * 匹配com.liner.controller包及其子包下的所有類的所有方法
     */
    @Pointcut("execution(public * com.liner.controller.*.*(..))")
    // 切入點(diǎn)    執(zhí)行    公有方法 任意返回值    包路徑   任何類 任何方法 任何參數(shù)
    public void pointCut() {}



    //2.織入內(nèi)容
    /**
     * 前置通知,目標(biāo)方法調(diào)用前被調(diào)用
     * 向切面方法前添加代碼,在方法運(yùn)行前輸出指定內(nèi)容
     */
    @Before("pointCut()")
    public void beforeAdvice(JoinPoint joinPoint) { //JoinPoint 會(huì)包含當(dāng)前切面的各種信息,連接點(diǎn)
        log.info("----------- 前置通知 -----------");
        Signature signature = joinPoint.getSignature(); //獲得當(dāng)前切面對(duì)應(yīng)方法的信息

        log.info("返回目標(biāo)方法的簽名:{}", signature);
        log.info("代理的是哪一個(gè)方法:{}", signature.getName());
        Object[] args = joinPoint.getArgs();
        log.info("獲取目標(biāo)方法的參數(shù)信息:{}", Arrays.asList(args));

        map.put("方法簽名", signature);
        map.put("代理方法", signature.getName());
        map.put("參數(shù)信息", Arrays.asList(args));

    }

    /**
     * 后置通知,也叫最終通知,目標(biāo)方法執(zhí)行完之后執(zhí)行
     */
    @After("pointCut()")
    public void afterAdvice() {
        log.info("----------- 后置通知 -----------");
    }

    /**
     * 后置返回通知
     * 如果參數(shù)中的第一個(gè)參數(shù)為JoinPoint,則第二個(gè)參數(shù)為返回值的信息
     * 如果參數(shù)中的第一個(gè)參數(shù)不為JoinPoint,則第一個(gè)參數(shù)為returning中對(duì)應(yīng)的參數(shù)
     * returning 只有目標(biāo)方法返回值與通知方法相應(yīng)參數(shù)類型時(shí)才能執(zhí)行后置返回通知,否則不執(zhí)行
     *
     * @param joinPoint
     * @param keys
     */
    @AfterReturning(pointcut = "pointCut()", returning = "keys")
    public void afterReturningAdvice(JoinPoint joinPoint, String keys) {
        log.info("~~~~~~~~~~ 后置返回通知 ~~~~~~~~~~");
        log.info("后置返回通知的返回值:{}", keys);
    }

    /**
     * 后置異常通知
     * 定義一個(gè)名字,該名字用于匹配通知實(shí)現(xiàn)方法的一個(gè)參數(shù)名,當(dāng)目標(biāo)方法拋出異常返回后,將把目標(biāo)方法拋出的異常傳給通知方法;
     * throwing 只有目標(biāo)方法拋出的異常與通知方法相應(yīng)參數(shù)異常類型時(shí)才能執(zhí)行后置異常通知,否則不執(zhí)行,
     *
     * @param e
     */
    @AfterThrowing(value = "pointCut()", throwing = "e")
    public void afterThrowingAdvice(Exception e) {
        log.info("~~~~~~~~~~ 后置異常通知 ~~~~~~~~~~");
        log.info(e.toString());
    }

    /**
     * 環(huán)繞通知
     * 環(huán)繞通知非常強(qiáng)大,可以決定目標(biāo)方法是否執(zhí)行,什么時(shí)候執(zhí)行,執(zhí)行時(shí)是否需要替換方法參數(shù),執(zhí)行完畢是否需要替換返回值。
     * 環(huán)繞通知第一個(gè)參數(shù)必須寫(xiě),且是org.aspectj.lang.ProceedingJoinPoint類型  JoinPoint的子接口,擁有更多方法
     * 這個(gè)方法還要有返回值,因?yàn)檎{(diào)用的切面的方法可能有返回值,環(huán)繞advice如果不返回這個(gè)值,調(diào)用者就接收不到這個(gè)值了
     * @param proceedingJoinPoint
     */
    @Around(value = "pointCut()")
    public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {
        log.info("----------- 環(huán)繞通知 -----------");
        log.info("環(huán)繞通知的目標(biāo)方法名:{}", proceedingJoinPoint.getSignature().getName());
        map.put("目標(biāo)方法", proceedingJoinPoint.getSignature().getName());
        try {
            return proceedingJoinPoint.proceed();   //ProceedingJoinPoint類型的參數(shù)具有調(diào)用切面方法的功能
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        } finally {
            log.info("---------- 環(huán)繞通知結(jié)束 -------------");
            writeLog(map);
        }
        return null;
    }

    /**
     * 打印日志
     * @param map
     */
    void writeLog(HashMap<String, Object> map) {
        try {
            long currentTimeMillis = System.currentTimeMillis();
            SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日-hh時(shí)mm分ss秒");
            Date date = new Date(currentTimeMillis);
            FileWriter fw = new FileWriter(filePath,true);
            fw.write("----------- 環(huán)繞通知 -----------"+    format.format(date)+  "\r\n");
            fw.write("環(huán)繞通知的目標(biāo)方法名:" + (String) map.get("目標(biāo)方法") +      "\r\n");
            fw.write("----------- 前置通知 -----------"+                          "\r\n");
            fw.write("返回目標(biāo)方法的簽名:" + (String) map.get("方法簽名").toString() +        "\r\n");
            fw.write("代理的是哪一個(gè)方法:" + (String) map.get("代理方法") +        "\r\n");
            fw.write("獲取目標(biāo)方法的參數(shù)信息:" + (String) map.get("參數(shù)信息").toString() +    "\r\n");
            fw.write("----------- 最終通知 -----------"+                          "\r\n");
            fw.write("---------- 環(huán)繞通知結(jié)束 -------------"+                     "\r\n");
            fw.flush();
            fw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

切面定義語(yǔ)法

 @Pointcut("execution(public * com.liner.controller.*.*(..))")

下面介紹切面定義表達(dá)式的詳細(xì)語(yǔ)法規(guī)則

語(yǔ)法模板:

execution(
modifier-pattern?
ret-type-pattern
declaring-type-pattern?
name-pattern(param-pattern)
throws-pattern?
)

?的是可選屬性,不帶?是必須寫(xiě)的

  • modifier-pattern:訪問(wèn)修飾符(可選)
  • ret-type-pattern:返回值類型(必寫(xiě)),一般為*
  • declaring-type-pattern:全路徑類名(可選)
  • name-pattern(param-pattern):方法名(必寫(xiě))
  • param-pattern:參數(shù)列表(必寫(xiě)),一般為..
  • throws-pattern:拋出的異常類型(可選)

小細(xì)節(jié)

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-DVE36Unh-1688283328058)(../AppData/Roaming/Typora/typora-user-images/image-20230702143314638.png)]

幾種通知圖標(biāo)不同:

  • 前置通知(before):

    [外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-Gs7nXB4n-1688283328059)(../AppData/Roaming/Typora/typora-user-images/image-20230702143521568.png)]

  • 后置通知(after):

    [外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-bvYpIIpl-1688283328060)(../AppData/Roaming/Typora/typora-user-images/image-20230702143808539.png)]

  • 異常通知(afterThrowing):

    [外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-NGPiH1E2-1688283328061)(../AppData/Roaming/Typora/typora-user-images/image-20230702144114022.png)]

  • 返回通知(afterReturning):

    [外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-BKLGGMgj-1688283328061)(../AppData/Roaming/Typora/typora-user-images/image-20230702144010730.png)]

  • 環(huán)繞通知(around):

    [外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-3NAaxAvc-1688283328062)(../AppData/Roaming/Typora/typora-user-images/image-20230702144223571.png)]

輸出日志成功

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-YfcEvstE-1688283328062)(../AppData/Roaming/Typora/typora-user-images/image-20230702142807461.png)]

原文鏈接:https://blog.csdn.net/weixin_65777087/article/details/131501526

  • 上一篇:沒(méi)有了
  • 下一篇:沒(méi)有了
欄目分類
最近更新