第二章:基於註解建立切面(案例一)
返回上一章節: 第一章:面向切面程式設計-AOP概念和相關術語
作者: 潘勇
2019/3/24 10:14:45
@all 本文為原創,轉載請註明出處
上一章節我們說到了Aop的概念和一些相關的術語,這一章節我們將基於以上的概念進行一個小案例的實戰。我們如果想要熟練的掌握,就需要多加練習。
案例的git地址: https://github.com/pydlove/study/tree/master/aop-demo-beyond
如果有相關問題可以新增QQ群:801340005,可以一起討論學習技術。
1、準備階段
我們本章基於AspectJ來完成的,AspectJ是一個優秀個AOP框架,當Spring Aop 不能滿足需求時,我們可以轉向更加的強大的AspectJ。
廢話不多說,我們直接準備案例需要的 jar 包,我這裡建立的並不是Maven專案,當然你也可以建立Maven專案。並且我使用的是Spring4,

案例需要的jar包
我這裡只對jar包做個簡單點介紹,畢竟不是我們的主線,不應該佔據過多的內容。主要分為兩部分,第一部分是Spring自帶的,可以參考下面的帖子:
第一部分包含22個jar包。
第二部分: 包含6個jar包
AspectJ: aspectjrt-1.6.9.jar、aspectjweaver-1.7.4.jar
資料處理: fastjson-1.2.56.jar (這個東西一旦你使用就會發現它的好處,非常強大,我到哪個專案都忘不了帶上它)(本章節用不到,後面使用)
Lombok: lombok-1.16.18.jar(它封裝很多註解能讓我們POJO類重複的東西簡化,例如:@Data,@Builder等)(本章節用不到,後面使用)
日誌: slf4j-api-1.7.26.jar、slf4j-simple-1.7.26.jar (本章節用不到,後面使用)
既然jar包準備好了,我們就開始第一個案例吧。
2、案例
這個案例我想實現的是:
某個投資方由於特別喜歡Beyond想舉辦一個晚會,請beyond上場表演,演出之前都有主持人報幕,這個是他的訴求。(這部分是投資方安排好的,列進投資方的計劃書裡了)
Beyond得知這個訊息之後,決定在晚會上和大家好好互動下。(這部分是投資方為安排的,沒有列進投資方的計劃書裡)

aop@demo@beyond
我們開始基於這個案例實現吧!
我們設計下需要哪些類:
投資方 :也就是測試類
晚會: 介面,提供表演這個方法,這個方法就是連線點
主持人: 切面類,在表演之前之後都需要報幕
Beyond: 晚會的主角,晚會介面的實現類,需要實現晚會的抽象方法
互動: 互動是附加內容,並未在計劃書裡,互動應該具備一個介面,有一個互動方法,再具備一個實現類,實現互動的內容。再基於其他形式引入到晚會中。
(注:引入是上一章節提到的概念)
AopConfig: 這個類主要是開始AspectJ的自動代理,並且還可以在這個類宣告我們切面的bean
晚會介面:

晚會介面
Beyond:
實現了晚會介面,實現了sing()方法,並且需要被@Component註解標註,因為需要建立SingerBeyond 這個bean。

Beyond
主持人:
主持人使我們整個案例的重點,支援人我們的切面類,負責在Beyond表演之前、之後、整個表演過程中(環繞)、表演異常這些情況下提供支援。
1、主持人類Compere 首先得宣告bean,之後被Spring容器管理,我們才能將自動將主持人注入到晚會的每一個過程,所以得使用@Component標註
2、主持人類Compere是切面,所以得用@Aspect註解標註,宣告該類是切面。宣告之後,該類不是普通的POJO類了,它是一個切面,定義了切面的具體行為。@Aspect是引用自org.aspectj.lang.annotation包下。上一章節我們說了切面主要包括:通知和切點,通知表明了what、when,切點表名了where。
3、該類中聲明瞭四個通知,分別是前置通知(Before)、環繞通知(Around)、返回通知(AfterReturning)、異常通知(AfterThrowing)
我們觀察四個通知都有相似處,@Before、@AfterReturning都有 execution(* com.study.pany.bean.aopDemo1.IEvening.sing(String, String)) && args(singer, song),@Around都有 execution(* com.study.pany.bean.aopDemo1.IEvening.sing(..)),@AfterThrowing有pointcut ="execution(* com.study.pany.bean.aopDemo1.IEvening.*(..))", throwing ="throwable"
這個是什麼呢?我們下面介紹。

4、我這裡只簡單分析下@Before註解,我們點開@Before註解。(詳細分析註解的活並不是本章應該乾的,本章只需要瞭解這些註解到底幹了些什麼。所以本章就不演示自定義註解,包括一些一些屬性介紹,放在其他章節,請留意以後內容的更新)

@Before
@Target({ElementType.METHOD}) 表示註解方法級別的
@Retention(RetentionPolicy.RUNTIME) 表示執行時載入
String value(); 重點,@Before("execution(* com.study.pany.bean.aopDemo1.IEvening.sing(String, String)) && args(singer, song)") 也可以寫成@Before(value ="execution(* com.study.pany.bean.aopDemo1.IEvening.sing(String, String)) && args(singer, song)");
@Before本身表明是的切面的when,而這個value的內容表明的是where,所以它是切點,被@Before標註的方法表明的是what。
所以我們現在知道了execution(* com.study.pany.bean.aopDemo1.IEvening.sing(String, String)) && args(singer, song)這個是切點。
String argNames()default ""; 引數名稱,字串型別,預設是""
5、對比@Before和@Around的value
execution(* com.study.pany.bean.aopDemo1.IEvening.sing(String, String)) && args(singer, song)
execution(* com.study.pany.bean.aopDemo1.IEvening.sing(..))
一張圖告訴你。

注:winthin() 和 execution() 類似,都是指定哪些連線點是切點的,但execution更強大一點,大可以作用到包,小可以作用到方法;而within 最小作用範圍是類
AopConfig:
通過@EnableAspectJAutoProxy開啟自動代理

投資方:
投資方就是我們的測試類,利用Junit啟動Spring的容器。當然也可以使用ClassPathXmlApplicationContext這個方式去啟動,這裡就不對ClassPathXmlApplicationContext啟動做介紹。畢竟我們的主題是基於註解的,這樣儘量都用註解的方式。
注意類上的@RunWith和@ContextConfiguration
@RunWith 是基於SpringJunit4ClassRunner這個類,當然我們也可以自定義自己的類去是繼承這個類,加入我們自己想要加入的邏輯,RunWith這裡用我們自己的類啟動。(這裡不做介紹,可以自行了解)
@ContextConfiguration 指定讀取的配置檔案,我們這裡配置檔案很簡單,只是設定哪個包下開啟自動掃描。
@Autowired 自動裝配,所以之前IEvening的實現類SingerBeyond上,我們需要使用@Component,否則這裡沒辦法注入。


applicationContext-beyond.xml
我們啟動看下結果:
我只在Before和AfterReturning 這裡加入邏輯去,@Around這裡沒有加入邏輯,這個我們後面做日誌管理案例的時候會著重介紹到。

如果我們需要測試異常之後切面會做出響應,我們需要改造下現有的類。這部分程式碼我直接截圖出來。




執行結果如下:

3、案例一的擴充(實現引入)
我們上面程式碼都是實現的是投資方安排好的內容,已經列進計劃書的內容,而我接下來要做的就是投資方計劃之外的,也就是Beyond的和大家互動。這部分沒辦法寫到計劃書裡面,因為Beyond沒有計劃書的原稿,沒有辦法修改,如何加入到環節中呢?
其實這也就是在我們日常開發中,可能有些核心的內容已經封裝好了,是以jar的形式提供給我們的,我們是沒辦法直接修改的,因為我們壓根就沒有原始碼,這時候我們如何將我們的內容引入呢?就由這個案例來揭曉吧。
我們上面說到引入互動,需要一個介面,一個實現類,然後通過某種形式引入到晚會上。但是晚會原來的介面是不會修改的。
首先建立介面:
IInteraction (互動)

IInteraction
InteractionImpl(互動實現類)

InteractionImpl
互動形式:
Game,互動是以遊戲的形式加入到晚會中。我們來介紹下Game。
1、我們的Game類同樣得宣告並建立bean,交給Spring容器來處理。
2、這個引入的類必須是以切面的形式引入。所以要使用@Aspect註解。
3、基於@DeclareParents這個註解引入到目標類裡。com.study.pany.bean.aopDemo1.IEvening 指的是目標類,我們需要將互動引入到晚會,晚會就是我們的目標類,“+”表示該類和它所有的子類。defaultImpl 表示選擇哪個類引入到目標類中,也就是具體互動什麼內容。
@DeclareParents(value ="com.study.pany.bean.aopDemo1.IEvening+", defaultImpl = InteractionImpl.class)
(本章節先只是講使用,後面章節再介紹,它是如何基於代理模式去實現的)

接下來我們使用投資方的類來呼叫試試,如下:

執行結果如下:

可見我們已經引入成功了。