天天看點

Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識

目錄,更新ing,學習Java的點滴記錄

  目錄放在這裡太長了,附目錄連結大家可以自由選擇檢視--------Java學習目錄

Spring知識

  一丶SpringIOC初步認識↓↓↓

第一篇---->初識Spring

  二丶SpringIOC深入↓↓↓

第二篇---->深入SpringIoC容器(一)

第三篇---->深入SpringIoC容器(二)

  三丶裝配SpringBean↓↓↓

第四篇---->依賴注入的方式

第五篇---->基于xml裝配Bean

第六篇---->基于注解裝配Bean

第七篇---->Spring Bean之間的關系

第八篇---->SpringBean的作用域

第九篇---->Spring 加載屬性(properties)檔案

第十篇---->Spring表達式(SpEL)

第十一篇---->Spring在xml中配置元件掃描

  四丶面向切面程式設計↓↓↓

第十二篇—>認識SpringAOP及底層原理

第十三篇—>使用@AspectJ注解開發AOP

第十四篇—>使用xml配置開發AOP

  五丶Spring中資料庫程式設計↓↓↓

第十五篇—>資料庫程式設計JdbcTemplate

  六丶Spring事務管理↓↓↓

第十六篇—>Spring事務管理初識

第十七篇—>程式設計式事務和聲明式事務

第十八篇—>事務ACID特性

第十九篇—>事務傳播行為

第二十篇—>事務隔離級别

5 使用@AspectJ注解開發Spring AOP

5.1 AOP簡介及準備環境

  • AspectJ:Java 社群裡最完整最流行的 AOP 架構.在 Spring2.0 以上版本中, 可以使用基于AspectJ 注解的形式實作AOP
  • 準備環境

      1) 要在 Spring 應用中使用 AspectJ 注解, 必須在 classpath 下包含 AspectJ 類庫: aopalliance.jar、aspectjweaver.jar 和 spring-aspects.jar,spring-aop.jar

      jar包資料:連結:https://pan.baidu.com/s/1i3LPve0Z3ap5oBRDTELCXw 提取碼:bk13

      2) 将 aop命名空間及對應xsd檔案添加到 根元素中.(具體命名空間查詢—>Spring配置檔案命名空間),要在 Spring IOC 容器中

    啟用 AspectJ 注解支援

    , 隻要在 Bean 配置檔案中

    定義一個空的 XML 元素 <aop:aspectj-autoproxy>

      
    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
      3) 當 Spring IOC 容器偵測到 Bean 配置檔案中的 aop:aspectj-autoproxy 元素時, 會自動為與 AspectJ 切面比對的 Bean 建立代理.
  • 其他說明

     &esmp;在 AspectJ 注解中, 切面隻是一個帶有 @Aspect 注解的 Java 類.要在 Spring 中聲明 AspectJ 切面, 隻需要在 IOC 容器中将切面聲明為 Bean 執行個體.

      通知是标注有某種注解的簡單的 Java 方法.

      AspectJ 支援 5 種類型的通知注解

5.2 其他知識

  • 使用注解@AspectJ開發AOP的時候的時候還需要了解@AspectJ注解的使用,各種通知的注解,切入點表達式等,這裡就不單獨拿出來挨個介紹了,直接通過後面的這個案例,用到之後立即說明,這樣了解起來也會更加直覺

5.3 選擇切點

  • Spring是方法級别的AOP架構,而我們主要也是以某個類的某個方法作為切點,用動态代理的理論來說,就是要攔截哪個方法織入對應AOP通知.
  • 首先建立一個User類,然後建立一個列印使用者資訊的接口及其實作類

      

    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
      
    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
  • 這時候我會将實作類中的

    printUser方法作為AOP的切點

    ,那麼底層動态代理的作用就是要為UserServiceImpl生成代理對象,然後攔截printUser()方法.于是就産生了各種AOP通知方法

5.4 建立切面

  • 選擇好了切點,那麼我們來建立切面,對于

    動态代理而言,它就如同是一個攔截器

    ,在Spring中隻要

    使用@AspectJ注解一個類,那麼SpringIOC容器就會認為這是一個切面

  • 建立UserAspect切面

      

    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
  • 上面畫框的就是AspectJ關于通知的注解,看名字我不說都能猜到了吧,這裡沒有添加環繞通知,可能是因為這個太強了,後面再抽出來講哈哈哈
  • 各種通知的對應注解解釋

      

    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
  • 強調一下

    傳回通知

      在傳回通知中, 隻要将

    returning 屬性

    添加到 @AfterReturning 注解中, 就可以通路連接配接點的傳回值(被調用方法的傳回值). 該屬性的值即為用來傳入傳回值的參數名稱.

      

    必須在通知方法的簽名中添加一個同名參數

    . 在運作時, Spring AOP 會通過這個參數傳遞傳回值.

      原始的切點表達式需要出現在

    pointcut 屬性

      

    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
  • 再強調一下

    異常通知

    :

      隻在連接配接點抛出異常時才執行異常通知

      

    将 throwing 屬性添加到 @AfterThrowing 注解中

    , 也可以通路連接配接點抛出的異常. Throwable 是所有錯誤和異常類的超類. 是以在異常通知方法可以捕獲到任何錯誤和異常.

      如果

    隻對某種特殊的異常類型感興趣

    , 可以将參數聲明為其他異常的參數類型. 然後通知就隻在抛出這個類型及其子類的異常時才被執行

      

    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
      
    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
  • 參考這個表,研究上面的切面類代碼就容易多了.還有一個需要注意的知識點—>注解中用到的切入點表達式,作用是告訴SpringAOP,需要攔截什麼類的什麼方法.

5.5 連接配接點

  • 下面談談Spring是如何判斷什麼方法是需要攔截的,畢竟你項目中設計的方法不可能都需要被攔截,這就涉及到連接配接點的問題.
  • 切入點表達式解釋:execution(* com.aop.aspectj.UserServiceImpl.printUser(…))

      

    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
  • 通過這種描述,全限定名 com.aop.aspectj.UserServiceImpl的類的printUser方法被攔截了,它就會按照AOP的通知的規則把方法織入到流程中.
  • 切入點表達式的時候非常靈活,下面幾個例子,大家可以過一遍

      

    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
  • AspectJ還包括很多訓示器,可以更為具體的使用切入點表達式

      

    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
  • 拿within舉個例子,下面的表達式就隻能比對到com.aop.aspectj.UserServiceImpl類下的printUser方法

      

    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
  • 其中,&&表示并且的含義,在xml中配置時需要使用and代替,運算符||可以用or代替,非運算符!可以用not代替
  • 讨論完了切入點表達式,現在我們回到切面類UserAspect類中,可以發現各種通知的切入點表達式都是一樣的,重複代碼多寫好幾遍,就應該有辦法将其統一簡化一下,這裡我們引入@Pointcut注解

      

    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
  • 這樣就可以做到重複使用一個簡單表達式取代多次寫複雜表達式了

5.6 測試AOP

  • 對Spring的Bean進行配置,采用Java注解方式

      

    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
      @EnableAspectJAutoProxy代表啟用AspectJ架構的自動代理,這個時候Spring會生成動态代理對象,然後就可以使用AOP,而getUserAspect方法可以生成一個切面執行個體
  • 對Spring的Bean進行配置,也可以采用xml方式

      

    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
      第一行作用等同于@EnableAspectJAutoProxy
  • 以上兩種方式都可以産生動态代理對象,進而組織切面,把各種通知放入流程中
  • 測試代碼

      

    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
  • 強調注意點!!!!

    ,關于上圖中代碼擷取Bean的方式,目前測試隻能使用這種寫法,有些小夥伴可能想到了下面這種寫法,但是運作時,發現報錯了,顯示找不到UserServiceImpl類型的Bean

      

    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
  • 但是當你打開UserServiceImpl類的代碼後發現明明添加了@Component注解,該注解不是可以将被注解的類變成Bean放入SpringIOC容器中嗎?為啥我明明注解了反而擷取不到了呢?其實你忘記了AOP底層很重要的知識點–>動态代理

      

    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
  • 下面讓我們通過斷點調試的方式,看一下UserSerive userSerive = ctx.getBean(UserSerive.class);拿到的究竟是什麼對象

      

    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
  • 哇,真相大白了,原來拿到的是一個代理對象,看來Spring底層是幫我們對UserServiceImpl類進行了轉換,用JDK動态代理的方式生成了新的代理對象,那麼奇怪的問題又來了–>我用UserServiceImpl擷取不到對象現在明白了,那麼為啥UserService為啥就可以呢?看來你對我本篇文章先講的動态代理案例中斷點調試的截圖沒有仔細看哦,

    采用JDK的動态代理,生成的代理對象擁有和被代理對象相同的接口

    ,而getBean可以使用多态擷取Bean示例,你傳入一個UserService接口類型,那麼就能比對到UserService接口實作類的Bean執行個體.

5.7 環繞通知

  • 環繞通知是SpringAOP

    最強大的通知

    ,他可以同時實作前置通知和後置通知.它保留了排程被代理對象原有方法的功能,強大又靈活,但可控性不強,如果不需要大量改變業務邏輯,一般而言并不需要使用它.
  • 下面在UserAspect接口中引入,并運作測試類執行

      

    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
      
    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
  • 但是需要注意的是有些通知之間執行順序是不一定的,如下圖(有興趣的同學可以看一下第十三篇,文章中有xml格式開發AOP的通知的執行順序,和本次注解的順序是不相同的)

      

    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識

5.8 織入

  • 織入是生成代理對象的過程.在前面提到的代碼示例中,選擇的切點所在的類都是擁有接口的類,而實際上即使沒有接口,Spring也能提供AOP的功能,是以

    是否擁有接口不是使用SpringAOP的一個強制要求

    .之前說動态代理的時候也提到過,使用JDK動态代理,必須擁有接口,而使用CGLib則不需要.于是Spring中存在一個規則:`當類的實作存在接口的時候,Spring将提供JDK動态代理,進而織入各個通知.當類不存在接口的時候沒有辦法使用JDK動态代理,Spring會采用CGLib來生成代理對象.
  • 我們現在重新修改上述代碼,将接口實作類取消實作接口,并修改Test類擷取Bean的方式,看看得到的Bean對象是否為CGLib生成的代理對象
  • 修改代碼和設定斷點

      

    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
  • 斷點調試

      

    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
  • 這裡可以很明顯看出,在對沒有接口實作的類實作代理時,Spring底層為我們使用了CGlib的代理模式
  • 動态代理對象是由SpringIOC容器根據描述生成的,一般不需要修改,對于我們來說,你隻需要明白AOP中的約定就可以輕松使用AOP了,Spring中建議使用接口程式設計.好處是實作定義和實作分離,更加靈活.

5.9 給通知傳遞參數

  • 之前使用各種通知的時候都沒有分析過參數的傳遞,隻是在各類通知的注解中聲明了一個切入點表達式去比對切點,實際開發中有時還是用的到傳遞被攔截方法的參數到通知中去的.
  • 修改切點的方法,讓方法帶有形參

      

    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
  • 現在将這個方法作為切點,使用切面進行攔截

      

    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
  • 通過使用args就可以實作參數的傳遞的功能,其他的通知傳遞參數的方式與該示例是一緻的.

5.10 引入

  • SpringAOP隻是通過動态代理技術,把各類通知織入到它所約定的流程中,而實際上,我們有時候會

    希望通過引入其他類的方法來得到更好的實作

    ,這時候就可以通過引入來操作了
  • 以之前的代碼為例,要求printUser方法在列印User時,需要要求使用者不為空是才列印,這時我們可以引入一個檢測目前User是否為空的類.

      

    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
  • 定義一個檢測User是否為空的接口及其實作類,該類非常簡單,這裡隻是使用一個淺顯易懂的例子幫助大家了解引入

      

    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
  • 現在需要修改切面類了,我們需要給它添加一個新的屬性

      

    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
  • 開始測試被攔截的方法,現在執行的時候會先判斷列印的User是否為空,如果不為空才會列印

      

    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
  • 使用強制轉換後可以把UserService轉化為UserCheck接口對象,然後就可以使用check方法了,而UserCheck調用的方法check,顯然就是通過UserCheckImpl來實作的.
  • 它的底層實作原理就是依賴于動态代理,由于上面代碼中 的UserServiceImpl是實作了接口的,是以Spring在運作時會選擇JDK動态代理模式,
  • 而JDK的動态代理可以将代理對象挂載到多個接口下面,是以說,隻要SpringAOP讓代理對象挂載到UserService和UserCheck接口下,那麼就可以把對應的Bean通過強制轉換,讓其在UserService和UserCheck直接互相轉換了.
  • 下面通過斷點測試驗證一下

      

    Spring架構(使用@AspectJ注解開發AOP)目錄,更新ing,學習Java的點滴記錄Spring知識
  • 調試結果中很明顯看出确實挂在了兩個接口,是以肯定能夠互相轉換,進而可以調用引入的方法,這樣就能通過引入功能,在原有基礎上增強Bean了.
  • 同樣的,之前提到過如果被代理對象沒有實作接口,Spring底層依舊會自動選用CGLib進行動态代理,CGLib中有Enhancer對象,他也有interfaces的屬性,同樣運作代理對象挂載到多個接口之下.