Java程式設計師從笨鳥到菜鳥之(七十四)細談Spring(六)spring之AOP基本概念和配置詳解
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
首先我們來看一下官方文件所給我們的關於AOP的一些概念性詞語的解釋:
切面(Aspect):一個關注點的模組化,這個關注點可能會橫切多個物件。事務管理是J2EE應用中一個關於橫切關注點的很好的例子。在Spring AOP中,切面可以使用基於模式)或者基於Aspect註解方式來實現。通俗點說就是我們加入的切面類(比如log類),可以這麼理解。
連線點(Joinpoint):在程式執行過程中某個特定的點,比如某方法呼叫的時候或者處理異常的時候。在Spring AOP中,一個連線點總是表示一個方法的執行。
通知(Advice):在切面的某個特定的連線點上執行的動作。其中包括了“around”、“before”和“after”等不同型別的通知(通知的型別將在後面部分進行討論)。許多AOP框架(包括Spring)都是以攔截器做通知模型,並維護一個以連線點為中心的攔截器鏈。
切入點(Pointcut):匹配連線點的斷言。通知和一個切入點表示式關聯,並在滿足這個切入點的連線點上執行(例如,當執行某個特定名稱的方法時)。切入點表示式如何和連線點匹配是AOP的核心:Spring預設使用AspectJ切入點語法。
引入(Introduction):用來給一個型別宣告額外的方法或屬性(也被稱為連線型別宣告(inter-type declaration
目標物件(Target Object): 被一個或者多個切面所通知的物件。也被稱做被通知(advised)物件。 既然Spring AOP是通過執行時代理實現的,這個物件永遠是一個被代理(proxied)物件。
AOP代理(AOP Proxy):AOP框架建立的物件,用來實現切面契約(例如通知方法執行等等)。在Spring中,AOP代理可以是JDK動態代理或者CGLIB代理。
織入(Weaving):把切面連線到其它的應用程式型別或者物件上,並建立一個被通知的物件。這些可以在編譯時(例如使用AspectJ
通知型別:
前置通知(Before advice):在某連線點之前執行的通知,但這個通知不能阻止連線點之前的執行流程(除非它丟擲一個異常)。
後置通知(After returning advice):在某連線點正常完成後執行的通知:例如,一個方法沒有丟擲任何異常,正常返回。
異常通知(After throwing advice):在方法丟擲異常退出時執行的通知。
最終通知(After (finally) advice):當某連線點退出的時候執行的通知(不論是正常返回還是異常退出)。
環繞通知(Around Advice):包圍一個連線點的通知,如方法呼叫。這是最強大的一種通知型別。環繞通知可以在方法呼叫前後完成自定義的行為。它也會選擇是否繼續執行連線點或直接返回它自己的返回值或丟擲異常來結束執行。
環繞通知是最常用的通知型別。和AspectJ一樣,Spring提供所有型別的通知,我們推薦你使用盡可能簡單的通知型別來實現需要的功能。例如,如果你只是需要一個方法的返回值來更新快取,最好使用後置通知而不是環繞通知,儘管環繞通知也能完成同樣的事情。用最合適的通知型別可以使得程式設計模型變得簡單,並且能夠避免很多潛在的錯誤。比如,你不需要在JoinPoint上呼叫用於環繞通知的proceed()方法,就不會有呼叫的問題。
spring AOP的實現
在spring2.5中,常用的AOP實現方式有兩種。第一種是基於xml配置檔案方式的實現,第二種是基於註解方式的實現。接下來,以具體的示例來講解這兩種方式的使用。下面我們要用到的例項是一個註冊,就有使用者名稱和密碼,我們利用AOP來實現在使用者註冊的時候實現在儲存資料之前和之後或者是丟擲異常時,在這些情況下都給他加上日誌。在這裡我們只講解AOP,所以我只把關鍵程式碼貼出來,不相干的就不貼了。
首先我們來看一下業務邏輯service層:
/** * RegisterService的實現類 * @author 曹勝歡 */public class RegisterServiceImpl implements RegisterService { private RegisterDao registerDao; public RegisterServiceImpl() {} /** 帶引數的構造方法 */ public RegisterServiceImpl(RegisterDao registerDao){ this.registerDao =registerDao; } public void save(String loginname, String password) { registerDao.save(loginname, password); throw new RuntimeException("故意丟擲一個異常。。。。"); } /** set方法 */ public void setRegisterDao(RegisterDao registerDao) { this.registerDao = registerDao;}}
對於業務系統來說,RegisterServiceImpl類就是目標實現類,它的業務方法,如save()方法的前後或程式碼會出現異常的地方都是AOP的連線點。
下面是日誌服務類的程式碼:
/** * 日誌切面類 * @author 曹勝歡 */public class LogAspect { //任何通知方法都可以將第一個引數定義為 org.aspectj.lang.JoinPoint型別 public void before(JoinPoint call) { //獲取目標物件對應的類名 String className = call.getTarget().getClass().getName(); //獲取目標物件上正在執行的方法名 String methodName = call.getSignature().getName(); System.out.println("前置通知:" + className + "類的" + methodName + "方法開始了"); } public void afterReturn() { System.out.println("後置通知:方法正常結束了"); } public void after(){ System.out.println("最終通知:不管方法有沒有正常執行完成,一定會返回的"); } public void afterThrowing() { System.out.println("異常丟擲後通知:方法執行時出異常了"); } //用來做環繞通知的方法可以第一個引數定義為org.aspectj.lang.ProceedingJoinPoint型別 public Object doAround(ProceedingJoinPoint call) throws Throwable { Object result = null; this.before(call);//相當於前置通知 try { result = call.proceed(); this.afterReturn(); //相當於後置通知 } catch (Throwable e) { this.afterThrowing(); //相當於異常丟擲後通知 throw e; }finally{ this.after(); //相當於最終通知 } return result; }}
這個類屬於業務服務類,如果用AOP的術語來說,它就是一個切面類,它定義了許多通知。Before()、afterReturn()、after()和afterThrowing()這些方法都是通知。
下面我們就來看具體配置,首先來看一下:
<1>.基於xml配置檔案的AOP實現:這種方式在實現AOP時,有4個步驟。
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd> <bean id="registerDaoImpl" class="com.zxf.dao.RegisterDaoImpl"/> <bean id="registerService" class="com.zxf.service.RegisterServiceImpl"> <property name=" registerDaoImpl " ref=" RegisterDaoImpl "/> </bean> <!-- 日誌切面類 --> <bean id="logAspectBean" class="com.zxf.aspect.LogAspect"/> <!-- 第1步: AOP的配置 --> <aop:config> <!-- 第2步:配置一個切面 --> <aop:aspect id="logAspect" ref="logAspectBean"> <!-- 第3步:定義切入點,指定切入點表示式 --> <aop:pointcut id="allMethod" expression="execution(* com.zxf.service.*.*(..))"/> <!-- 第4步:應用前置通知 --> <aop:before method="before" pointcut-ref="allMethod" /> <!-- 第4步:應用後置通知 --> <aop:after-returning method="afterReturn" pointcut-ref="allMethod"/> <!-- 第4步:應用最終通知 --> <aop:after method="after" pointcut-ref="allMethod"/> <!-- 第4步:應用丟擲異常後通知 --> <aop:after-throwing method="afterThrowing" pointcut-ref="allMethod"/> <!-- 第4步:應用環繞通知 --> <!-- <aop:around method="doAround" pointcut-ref="allMethod" /> --> </aop:aspect> </aop:config></beans>
上述配置針對切入點應用了前置、後置、最終,以及丟擲異常後通知。這樣在測試執行RegisterServiceImpl類的save()方法時,控制檯會有如下結果輸出:
前置通知:com.zxf.service.RegisterServiceImpl類的save方法開始了。
針對MySQL的RegisterDao實現中的save()方法。
後置通知:方法正常結束了。
最終通知:不管方法有沒有正常執行完成,一定會返回的。
下面我們在來看一下第二種配置方式:
<2>基於註解的AOP的實現
首先建立一個用來作為切面的類LogAnnotationAspect,同時把這個類配置在spring的配置檔案中。
在spring2.0以後引入了JDK5.0的註解Annotation的支援,提供了對AspectJ基於註解的切面的支援,從而 更進一步地簡化AOP的配置。具體的步驟有兩步。
Spring的配置檔案是如下的配置:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd> <bean id="registerDao" class="com.zxf.dao.RegisterDaoImpl"/> <bean id="registerService" class="com.zxf.service.RegisterServiceImpl"> <property name="registerDao" ref="registerDao"/> </bean> <!-- 把切面類交由Spring容器來管理 --> <bean id="logAspectBean" class="com.zxf.aspect.LogAnnotationAspect"/> <!-- 啟用spring對AspectJ註解的支援 --> <aop:aspectj-autoproxy/></beans>
這是那個切面的類LogAnnotationAspect
/** * 日誌切面類 */@Aspect //定義切面類public class LogAnnotationAspect { @SuppressWarnings("unused") //定義切入點,提供一個方法,這個方法的名字就是改切入點的id @Pointcut("execution(* com.zxf.service.*.*(..))") private void allMethod(){} //針對指定的切入點表示式選擇的切入點應用前置通知 @Before("execution(* com. zxf.service.*.*(..))") public void before(JoinPoint call) { String className = call.getTarget().getClass().getName(); String methodName = call.getSignature().getName(); System.out.println("【註解-前置通知】:" + className + "類的" + methodName + "方法開始了"); } //訪問命名切入點來應用後置通知 @AfterReturning("allMethod()") public void afterReturn() { System.out.println("【註解-後置通知】:方法正常結束了"); } //應用最終通知 @After("allMethod()") public void after(){ System.out.println("【註解-最終通知】:不管方法有沒有正常執行完成," + "一定會返回的"); } //應用異常丟擲後通知 @AfterThrowing("allMethod()") public void afterThrowing() { System.out.println("【註解-異常丟擲後通知】:方法執行時出異常了"); } //應用周圍通知 //@Around("allMethod()") public Object doAround(ProceedingJoinPoint call) throws Throwable{ Object result = null; this.before(call);//相當於前置通知 try { result = call.proceed(); this.afterReturn(); //相當於後置通知 } catch (Throwable e) { this.afterThrowing(); //相當於異常丟擲後通知 throw e; }finally{ this.after(); //相當於最終通知 } return result; }}
備註:輸出結果和前面的一樣。
你好! 這是你第一次使用 **Markdown編輯器** 所展示的歡迎頁。如果你想學習如何使用Markdown編輯器, 可以仔細閱讀這篇文章,瞭解一下Markdown的基本語法知識。
新的改變
我們對Markdown編輯器進行了一些功能拓展與語法支援,除了標準的Markdown編輯器功能,我們增加了如下幾點新功能,幫助你用它寫部落格:
- 全新的介面設計 ,將會帶來全新的寫作體驗;
- 在創作中心設定你喜愛的程式碼高亮樣式,Markdown 將程式碼片顯示選擇的高亮樣式 進行展示;
- 增加了 圖片拖拽 功能,你可以將本地的圖片直接拖拽到編輯區域直接展示;
- 全新的 KaTeX數學公式 語法;
- 增加了支援甘特圖的mermaid語法1 功能;
- 增加了 多螢幕編輯 Markdown文章功能;
- 增加了 焦點寫作模式、預覽模式、簡潔寫作模式、左右區域同步滾輪設定 等功能,功能按鈕位於編輯區域與預覽區域中間;
- 增加了 檢查列表 功能。
功能快捷鍵
撤銷:Ctrl/Command + Z 重做:Ctrl/Command + Y 加粗:Ctrl/Command + B 斜體:Ctrl/Command + I 標題:Ctrl/Command + Shift + H 無序列表:Ctrl/Command + Shift + U 有序列表:Ctrl/Command + Shift + O 檢查列表:Ctrl/Command + Shift + C 插入程式碼:Ctrl/Command + Shift + K 插入連結:Ctrl/Command + Shift + L 插入圖片:Ctrl/Command + Shift + G
合理的建立標題,有助於目錄的生成
直接輸入1次#,並按下space後,將生成1級標題。
輸入2次#,並按下space後,將生成2級標題。
以此類推,我們支援6級標題。有助於使用TOC
語法後生成一個完美的目錄。
如何改變文字的樣式
強調文字 強調文字
加粗文字 加粗文字
標記文字
刪除文字
引用文字
H2O is是液體。
210 運算結果是 1024.
插入連結與圖片
連結: link.
圖片:
帶尺寸的圖片:
當然,我們為了讓使用者更加便捷,我們增加了圖片拖拽功能。
如何插入一段漂亮的程式碼片
去部落格設定頁面,選擇一款你喜歡的程式碼片高亮樣式,下面展示同樣高亮的 程式碼片
.
// An highlighted block var foo = 'bar';
生成一個適合你的列表
- 專案
- 專案
- 專案
- 專案
- 專案1
- 專案2
- 專案3
- 計劃任務
- 完成任務
建立一個表格
一個簡單的表格是這麼建立的:
專案 | Value |
---|---|
電腦 | $1600 |
手機 | $12 |
導管 | $1 |
設定內容居中、居左、居右
使用:---------:
居中
使用:----------
居左
使用----------:
居右
第一列 | 第二列 | 第三列 |
---|---|---|
第一列文字居中 | 第二列文字居右 | 第三列文字居左 |
SmartyPants
SmartyPants將ASCII標點字元轉換為“智慧”印刷標點HTML實體。例如:
TYPE | ASCII | HTML |
---|---|---|
Single backticks | 'Isn't this fun?' |
‘Isn’t this fun?’ |
Quotes | "Isn't this fun?" |
“Isn’t this fun?” |
Dashes | -- is en-dash, --- is em-dash |
– is en-dash, — is em-dash |
建立一個自定義列表
- Markdown
- Text-to-HTML conversion tool
- Authors
- John
- Luke
如何建立一個註腳
一個具有註腳的文字。2
註釋也是必不可少的
Markdown將文字轉換為 HTML。
KaTeX數學公式
您可以使用渲染LaTeX數學表示式 KaTeX:
Gamma公式展示 是通過尤拉積分
你可以找到更多關於的資訊 LaTeX 數學表示式here.
新的甘特圖功能,豐富你的文章
gantt
dateFormat YYYY-MM-DD
title Adding GANTT diagram functionality to mermaid
section 現有任務
已完成 :done, des1, 2014-01-06,2014-01-08
進行中 :active, des2, 2014-01-09, 3d
計劃一 : des3, after des2, 5d
計劃二 : des4, after des3, 5d
- 關於 甘特圖 語法,參考 這兒,
UML 圖表
可以使用UML圖表進行渲染。 Mermaid. 例如下面產生的一個序列圖::
這將產生一個流程圖。:
- 關於 Mermaid 語法,參考 這兒,
FLowchart流程圖
我們依舊會支援flowchart的流程圖:
- 關於 Flowchart流程圖 語法,參考 這兒.
匯出與匯入
匯出
如果你想嘗試使用此編輯器, 你可以在此篇文章任意編輯。當你完成了一篇文章的寫作, 在上方工具欄找到 文章匯出 ,生成一個.md檔案或者.html檔案進行本地儲存。
匯入
如果你想載入一篇你寫過的.md檔案或者.html檔案,在上方工具欄可以選擇匯入功能進行對應副檔名的檔案匯入, 繼續你的創作。