Spring中AOP簡介與使用
Spring中AOP簡介與使用
什麽是AOP?
Aspect Oriented Programming(AOP),多譯作 “面向切面編程”,也就是說,對一段程序,從側面插入,進行操做。即通過預編譯方式和運行期動態代理實現在不修改源代碼的情況下給程序動態統一添加功能的一種技術。
為什麽要用AOP?
日誌記錄,性能統計,安全控制,事務處理,異常處理等等。例如日誌記錄,在程序運行的某些節點上添加記錄執行操作狀態的一些代碼,獲取執行情況。而通過切面編程,我們將這些插入的內容分離出來,將它們獨立到業務邏輯的方法之外,進而使這些行為的時候不影響業務邏輯的執行。
如何使用AOP?
下面我們以一個簡單計算題目的例子用日誌記錄的方法演示一下面向切面編程。
(同時我們使用到Junit4來測試程序)
環境: jdk1.8
新建Dynamic Web Project
Jar包: (包的版本不定,可以根據個人開發需求添加,下面基本為必須包)
-com.springsource.net.sf.cglib-2.2.0.jar
-com.springsource.org.aopalliance-1.0.0.jar
-com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
-commons-logging-1.1.3.jar
-spring-aop-4.0.0.RELEASE.jar
-spring-aspects-4.0.0.RELEASE.jar
-spring-beans-4.0.0.RELEASE.jar
-spring-context-4.0.0.RELEASE.jar
-spring-core-4.0.0.RELEASE.jar
-spring-expression-4.0.0.RELEASE.jar
以下分兩種方式來說明
一、註解的方式
開發步驟:
1、創建方法類
這個類中寫了主要的業務操作步驟,選取了加法和出發作為案例(除法較為典型)
1 package da.wei.aop; 2 import org.springframework.stereotype.Component; 3 4 @Component //普通類的註解配置spring.xml的配置,加入IOC容器5 public class MathTestImpl { 6 public Integer add(int i,int j){ 7 System.out.println("執行add,參數i為: "+i+" ,參數j為: "+j+" ,結果為: "+(i+j)); 8 return (i+j); 9 } 10 11 public Integer div(int i,int j){ 12 System.out.println("執行div,參數i為: "+i+" ,參數j為: "+j+" ,結果為: "+(i/j)); 13 return (i/j); 14 } 15 }
該方法中需要強調一點規範問題,在使用函數返回值類型或者類的屬性設置時,使用包裝類型(Integer)來代替基本類型(int)可以避免不少錯誤。
2、創建多個方法的切面類
在這個切面類中,我們將每個通知類型的內容都單獨列出來了,每一層都羅列其執行的效果。
其中有以下幾個知識點
1)、切點註釋@PointCut 通過配置切點方法可以將切點統一使用,減少了代碼量,提高開發效率。
2)、切入點表達式
execution(public Integer da.wei.aop.MathTestImpl.add(int, int) )
此處用到的時詳細的有制定切唯一的方法,開發中我們不可能只有一個方法,因此可以用’*’通配符的方式表示,同時,方法返回值類型、權限修飾符、參數列表、包名、也可能不唯一,因此,可以優化為通配格式:
execution(* *.*(..)) 第一個’*’表示了權限修飾符以及方法返回值類型‘
3)、Before 中JoinPoint參數
Before 中JoinPoint參數可以獲取切入點處方法的幾乎全部信息,其中的方法名以及參數列表是常用信息
4)、AfterReturning 中的returning參數
AfterReturning 中的returning參數是用來接收切入方法的返回值的,其參數名需要其修飾方法體的參數名相同,同時由於參數類型不定,需要設為Object類型的
5)、Throwing 中的throwing參數
Throwing 中的throwing參數,用來獲取異常信息值;其中throwing= “參數名a” 需要與方法中的參數相等對應。
1 package da.wei.aop; 2 3 import java.util.Arrays; 4 import java.util.List; 5 6 import org.aspectj.lang.JoinPoint; 7 import org.aspectj.lang.Signature; 8 import org.aspectj.lang.annotation.After; 9 import org.aspectj.lang.annotation.AfterReturning; 10 import org.aspectj.lang.annotation.AfterThrowing; 11 import org.aspectj.lang.annotation.Aspect; 12 import org.aspectj.lang.annotation.Before; 13 import org.aspectj.lang.annotation.Pointcut; 14 import org.springframework.core.annotation.Order; 15 import org.springframework.stereotype.Component; 16 /* 17 * @Component 普通類的註解配置spring.xml的配置,加入IOC容器 18 * @Aspect 標識出此類為切面方法類 19 * Order 是為了指定在多個切面類的情況下,其執行的順序; 20 * value值為int類型的,值越小越先執行 21 */ 22 @Component 23 @Aspect 24 @Order(value=100) 25 public class MathAspectMutil { 26 //設置切點位置 此處設置的是add(int,int)方法 27 @Pointcut(value="execution(public Integer da.wei.aop.MathTestImpl.add(int, int) )") 28 public void getPointCut(){} 29 30 //註解,設置此方法是‘前置通知’ 31 @Before(value="getPointCut()") 32 public void before(JoinPoint point){ 33 System.out.println("[MathAspectMutil]【Before日誌】"); 34 //獲取插入點的參數 35 Object[] args = point.getArgs(); 36 //獲取方法簽名 37 Signature signature = point.getSignature(); 38 //獲取方法名 39 String name = signature.getName(); 40 //將參數數組傳為list 41 List<Object> asList = Arrays.asList(args); 42 System.out.println("方法簽名 : "+signature); 43 System.out.println("方法名為 : "+name+"方法體參數列表 : "+asList); 44 } 45 46 /* 47 * 註解,設置此處是‘後置通知’ 48 * 此處可以看出其value值與上面before方法的不一樣 49 * 實則是一樣的,只是通過PointCut將其統一封裝使用而已,效果相同 50 */ 51 @After(value="execution(public Integer da.wei.aop.MathTestImpl.add(int, int) )") 52 public void after(){ 53 System.out.println("[MathAspectMutil]【After日誌】"); 54 } 55 56 //註解,設置此方法是‘返回通知’ 57 @AfterReturning(value="getPointCut()",returning="result") 58 public void afterReturning(JoinPoint point,Object result){ 59 System.out.println("[MathAspectMutil]【AfterReturning日誌】"); 60 //獲取插入點的參數 61 Object[] args = point.getArgs(); 62 //獲取方法簽名 63 Signature signature = point.getSignature(); 64 //獲取方法名 65 String name = signature.getName(); 66 //將參數數組傳為list 67 List<Object> asList = Arrays.asList(args); 68 System.out.println("方法簽名 : "+signature); 69 System.out.println("方法名為 : "+name+"方法體參數列表 : "+asList+" ,執行結果為: "+result); 70 } 71 72 //註解,設置此方法是‘異常通知’ 73 @AfterThrowing(value="getPointCut()",throwing="ex") 74 public void afterThrowing(Throwable ex){ 75 System.out.println("[MathAspectMutil]【AfterThrowing日誌】"); 76 System.out.println("錯誤信息為 : "+ex.getMessage()); 77 } 78 }
3、創建Around方法的切面類
Around方法其就是對上面四種通知的合並,在環繞通知中上面的四種通知都有體現到
通過對下面代碼的分析就可以基本了解其運行結果了
1 package da.wei.aop; 2 import java.util.Arrays; 3 import java.util.List; 4 5 import org.aspectj.lang.ProceedingJoinPoint; 6 import org.aspectj.lang.Signature; 7 import org.aspectj.lang.annotation.Around; 8 import org.aspectj.lang.annotation.Aspect; 9 import org.aspectj.lang.annotation.Pointcut; 10 import org.springframework.core.annotation.Order; 11 import org.springframework.stereotype.Component; 12 13 @Component 14 @Aspect 15 @Order(value=5) 16 public class MathAspectAround { 17 //設置切點位置 此處設置的是add(int,int)方法 18 @Pointcut(value="execution(public Integer da.wei.aop.MathTestImpl.add(int, int) )") 19 public void getPointCut(){} 20 21 @Around(value="getPointCut()") 22 public Object around(ProceedingJoinPoint pJoinPoint){ 23 //獲取插入點的參數 24 Object[] args = pJoinPoint.getArgs(); 25 //獲取方法簽名 26 Signature signature = pJoinPoint.getSignature(); 27 //獲取方法名 28 String name = signature.getName(); 29 //將參數數組傳為list 30 List<Object> asList = Arrays.asList(args); 31 Object result = null; 32 try { 33 try{ 34 System.out.println("[MathAspectAround]【Before】..."); 35 System.out.println("方法簽名 : "+signature); 36 System.out.println("方法名為 : "+name+"方法體參數列表 : "+asList); 37 //獲得結果 38 result = pJoinPoint.proceed(args); 39 }finally { 40 System.out.println("[MathAspectAround]【After】...."); 41 } 42 System.out.println("[MathAspectAround]【AfteReturning】..結果為"+result+"...."); 43 } catch (Throwable e) { 44 // TODO Auto-generated catch block 45 System.out.println("[MathAspectAround]【Throwing】..原因為 "+e.getMessage()); 46 } 47 return result; 48 } 49 }
4、配置spring.xml
我這裏的spring配置文件名為applicationContext.xml
1 <!-- 掃描包,創建Bean對象 --> 2 <context:component-scan base-package="da.wei.aop"></context:component-scan> 3 <!-- 啟用 切面編程註解 --> 4 <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
5、Junit中代碼編寫
此處講解AOP的使用至於IOC的使用就不再贅述。
1 @Test 2 public void test() { 3 //獲取applicationContext配置信息,主要用於獲得IOC容器中的類對象 4 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); 5 //獲取bean對象 6 MathTestImpl bean = (MathTestImpl) applicationContext.getBean("mathTestImpl"); 7 /* 8 * 調用方法 9 * 由於在配置裏只編寫了針對add的切入點這裏只執行add方法 10 * 在之後的xml配置的方法中執行div方法 11 */ 12 bean.add(1,2); 13 //bean.div(10, 2); 14 }
6、執行結果
從執行結果中我們能看到已經正確執行了,同時我們也要註意到兩種方式的執行順序。
由於我們設置MathAspectMutil的Order為100 比MathAspectAround的5大因此MathAspectAround先執行,當其執行完before之後釋放的方法又被MathAspectMutil獲取,當MathAspectMutil執行完全部之後MathAspectAround再執行其他的方法,類似於攔截器的運行順序。
二、spring.xml配置的方式
spring.xml配置的方式其類的建立與上面相同,只是需要去除所有的註解,使用簡單的方法,簡單的類
1、其中的applicationContext.xml配置如下
1 <bean id="mathTestImpl" class="da.wei.aop.MathTestImpl"></bean> 2 <bean id="mathAspectMutil" class="da.wei.aop.MathAspectMutil"></bean> 3 <bean id="mathAspectAround" class="da.wei.aop.MathAspectAround"></bean> 4 <aop:config > 5 <!-- 第一個 好多方法的切面類 --> 6 <aop:pointcut expression="execution(* da.wei.aop.MathTestImpl.*(..))" id="myPointCut"/> 7 <aop:aspect ref="mathAspectMutil" order="10"> 8 <aop:before method="before" pointcut-ref="myPointCut" /> 9 <aop:after method="after" pointcut-ref="myPointCut"/> 10 <aop:after-returning method="afterReturning" pointcut-ref="myPointCut" returning="result"/> 11 <aop:after-throwing method="afterThrowing" pointcut-ref="myPointCut" throwing="ex"/> 12 </aop:aspect> 13 <!-- Around --> 14 <aop:aspect ref="mathAspectAround" order="5"> 15 <aop:around method="around" pointcut-ref="myPointCut" /> 16 </aop:aspect> 17 </aop:config>
2、Junit代碼如下
1 @Test 2 public void test() { 3 //獲取applicationContext配置信息,主要用於獲得IOC容器中的類對象 4 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); 5 //獲取bean對象 6 MathTestImpl bean = (MathTestImpl) applicationContext.getBean("mathTestImpl"); 7 bean.div(10, 2); 8 System.out.println(); 9 bean.div(10, 0); //此處我們測試了異常情況 10 }
3、執行結果
從結果總我們可以看出當執行異常出現的時候,會執行性Throwing而不執行AfterReturning,這個也可以在Around的代碼中可以看出。
以上是我最近學習AOP切面編程的一點總結,內容多為代碼,總結不足,不過開發與思路過程在註釋中有體現,希望能給大家一些幫助。
同時歡迎過路大神指點批評。
Spring中AOP簡介與使用