Spring知識點總結(四)之SpringAOP基礎
1. Spring aop中的基本概念
• 連線點(Joinpoint):在程式執行過程中某個特定的點,比如某方法呼叫的時候或者處理異常的時候。在Spring AOP中,一個連線點總是表示一個方法的執行。
通俗講:
層於層之間呼叫的過程中,目標層中可供呼叫的方法,就稱之為連線點。
• 切入點(Pointcut):匹配連線點的斷言。通知和一個切入點表示式關聯,並在滿足這個切入點的連線點上執行(例如,當執行某個特定名稱的方法時)。切入點表示式如何和連線點匹配是AOP的核心:Spring預設使用AspectJ切入點語法。
通俗講:
在連線點的基礎上 增加上切入規則 選擇出需要進行增強的切入點 這些基於切入規則選出來的連線點 就稱之為切入點。
• 切面(Aspect):一個關注點的模組化,這個關注點可能會橫切多個物件。事務管理是J2EE應用中一個關於橫切關注點的很好的例子。在Spring AOP中,切面可以使用基於模式)或者基於@Aspect註解的方式來實現。
通俗講:
狹義上就是 當spring攔截下切入點後 將這些切入點 交給 處理類 進行功能的增強,這個處理類就稱之為切面。
廣義上來講 將spring底層的代理 切入點 和 處理類 加在一起 實現的 對層與層之間呼叫過程進行增強的機制 稱之為切面。
• 通知(Advice):在切面的某個特定的連線點上執行的動作。其中包括了“around”、“before”和“after”等不同型別的通知(通知的型別將在後面部分進行討論)。許多AOP框架(包括Spring)都是以攔截器做通知模型,並維護一個以連線點為中心的攔截器鏈。
通俗講:
在spring底層的代理攔截下切入點後,將切入點交給切面類,切面類中就要有處理這些切入點的方法,這些方法就稱之為通知(也叫增強 增強方法)。針對於切入點執行的過程,通知還分為不同的型別,分別關注切入點在執行過程中的不同的時機。
• 目標物件(Target Object): 被一個或者多個切面所通知的物件。也被稱做被通知(advised)物件。 既然Spring AOP是通過執行時代理實現的,這個物件永遠是一個被代理(proxied)物件。
通俗講:
就是真正希望被訪問到的物件。spring底層的動態代理對他進行了代理,具體能不能真的訪問到目標物件,或在目標物件真正執行之前和之後是否做一些額外的操作,取決於切面。
2. spring的aop實現過程
a. 匯入aop相關開發包
b. 建立一個切面類
c. 定義通知
d. 定義一個連線點
e. 配置切入點
在MyEclipse中匯入aop的schema約束檔案,以便於在配置檔案中可以提示標籤。
在其中配置切入點:
f. 定義切面
g. 執行方法,發現切面確實起作用
3. 切入點表示式
a. within表示式
通過類名進行匹配 粗粒度的切入點表示式
within(包名.類名)
則這個類中的所有的方法都會別表示式識別,成為切入點。
在within表示式中可以使用*號匹配符,匹配指定包下所有的類,注意,只匹配當前包,不包括當前包的子孫包。
在within表示式中也可以用*號匹配符,匹配包
在within表示式中也可以用..*號匹配符,匹配指定包下及其子孫包下的所有的類
b. execution()表示式
語法:execution(返回值型別 包名.類名.方法名(引數型別,引數型別…))
例子1:
<aop:pointcut expression=
"execution(void cn.tedu.service.UserServiceImpl.addUser(java.lang.String))" id="pc1"/>
該切入點規則表示,切出指定包下指定類下指定名稱指定引數指定返回值的方法。
例子2:
<aop:pointcut expression="execution(* cn.tedu.service.*.query())" id="pc1"/>
該切入點規則表示,切出指定包下所有的類中的query方法,要求無參,但返回值型別不限。
例子3:
<aop:pointcut expression="execution(* cn.tedu.service..*.query())" id="pc1"/>
該切入點規則表示,切出指定包及其子孫包下所有的類中的query方法,要求無參,但返回值型別不限。
例子4:
<aop:pointcut expression="execution(* cn.tedu.service..*.query(int,java.lang.String))" id="pc1"/>
該切入點規則表示,切出指定包及其子孫包下所有的類中的query方法,要求引數為int java.langString型別,但返回值型別不限。
例子5:
<aop:pointcut expression="execution(* cn.tedu.service..*.query(..))" id="pc1"/>
該切入點規則表示,切出指定包及其子孫包下所有的類中的query方法,引數數量及型別不限,返回值型別不限。
例子6:
<aop:pointcut expression="execution(* cn.tedu.service..*.*(..))" id="pc1"/>
該切入點規則表示,切出指定包及其子孫包下所有的類中的任意方法,引數數量及型別不限,返回值型別不限。這種寫法等價於within表示式的功能。
4. SpringAOP的原理
Spring會在使用者獲取物件時,生成目標物件的代理物件,之後根據切入點規則,匹配使用者連線點,得到切入點,當切入點被呼叫時,不會直接去找目標物件,而是通過代理物件攔截之後交由切面類中的指定的通知執行來進行增強。
Spring自動為目標物件生成代理物件,預設情況下,如果目標物件實現過介面,則採用java的動態代理機制,如果目標物件沒有實現過介面,則採用cglib動態代理。開發者可以可以在spring中進行配置,要求無論目標物件是否實現過介面,都強制使用cglib動態代理。
5. Spring的五大通知型別
a. 前置通知
在目標方法執行之前執行執行的通知
前置通知方法,可以沒有引數,也可以額外接收一個JoinPoint,Spring會自動將該物件傳入,代表當前的連線點,通過該物件可以 獲取目標物件 和 目標方法相關的資訊。
注意,如果接收JoinPoint,必須保證其為方法的第一個引數,否則報錯。
b. 環繞通知
在目標方法執行之前和之後都可以執行額外程式碼的通知。
在環繞通知中必須顯式的呼叫目標方法,目標方法才會執行,這個顯式呼叫時通過ProceedingJoinPoint來實現的,可以在環繞通知中接收一個此型別的形參,spring容器會自動將該物件傳入,注意這個引數必須處在環繞通知的第一個形參位置。
**要注意,只有環繞通知可以接收ProceedingJoinPoint,而其他通知只能接收JoinPoint。
環繞通知需要返回返回值,否則真正呼叫者將拿不到返回值,只能得到一個null。
環繞通知有
控制目標方法是否執行、有控制是否返回值、甚至改變返回值
的能力
環繞通知雖然有這樣的能力,但一定要慎用,不是技術上不可行,而是要小心不要破壞了軟體分層的“高內聚 低耦合”的目標。
c. 後置通知
在目標方法執行之後執行的通知。
在後置通知中也可以選擇性的接收一個JoinPoint來獲取連線點的額外資訊,但是這個引數必須處在引數列表的第一個。
在後置通知中,還可以通過配置獲取返回值
一定要保證JoinPoint處在引數列表的第一位,否則拋異常
d. 異常通知
在目標方法丟擲異常時執行的通知
配置方法:
可以配置傳入JoinPoint獲取目標物件和目標方法相關資訊,但必須處在引數列表第一位
另外,還可以配置引數,讓異常通知可以接收到目標方法丟擲的異常物件
e. 最終通知
是在目標方法執行之後執行的通知。和後置通知不同之處在於,後置通知是在方法正常返回後執行的通知,如果方法沒有正常返-例如丟擲異常,則後置通知不會執行。而最終通知無論如何都會在目標方法呼叫過後執行,即使目標方法沒有正常的執行完成。另外,後置通知可以通過配置得到返回值,而最終通知無法得到。
配置方式:
最終通知也可以額外接收一個JoinPoint引數,來獲取目標物件和目標方法相關資訊,但一定要保證必須是第一個引數。
f. 五種通知執行的順序
i. 在目標方法沒有丟擲異常的情況下
前置通知
環繞通知的呼叫目標方法之前的程式碼
目標方法
環繞通知的呼叫目標方法之後的程式碼
後置通知
最終通知
ii. 在目標方法丟擲異常的情況下:
前置通知
環繞通知的呼叫目標方法之前的程式碼
目標方法 丟擲異常 異常通知
最終通知
iii. 如果存在多個切面:
多切面執行時,採用了責任鏈設計模式。
切面的配置順序決定了切面的執行順序,多個切面執行的過程,類似於方法呼叫的過程,在環繞通知的proceed()執行時,去執行下一個切面或如果沒有下一個切面執行目標方法,從而達成了如下的執行過程:
如果目標方法丟擲異常:
g. 五種通知的常見使用場景
前置通知 記錄日誌(方法將被呼叫)
環繞通知 控制事務 許可權控制
後置通知 記錄日誌(方法已經成功呼叫)
異常通知 異常處理 控制事務
最終通知 記錄日誌(方法已經呼叫,但不一定成功)
6. AOP的註解方式實現
spring也支援註解方式實現AOP,相對於配置檔案方式,註解配置更加的輕量級,配置、修改更加方便。
a. 開啟AOP的註解配置方式
b. 將制定的類標誌位一個切面
c. 配置通知 制定切入點規則
前置通知 @Before
環繞通知 @Around
後置通知 @AfterReturning
異常通知 @AfterThrowing
最終通知 @After
**通過註解的配置 等價於 配置檔案的配置
d. 如果一個切面中多個通知 重複使用同一個切入點表示式,則可以將該切入點表示式單獨定義,後續引用,注意,在當前切面中通過註解定義的切入點只在當前切面中起作用,其他切面看不到。
e. 在後置通知的註解中,也可以額外配置一個returning屬性,來指定一個引數名接受目標方法執行後的返回值
f. 在異常通知的註解中,也可以額外配置一個throwing屬性,來指定一個引數名接受目標方法丟擲的異常物件