引言:java
AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程,經過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術.AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生範型。利用AOP能夠對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度下降,提升程序的可重用性,同時提升了開發的效率。算法
在Spring AOP中業務邏輯僅僅只關注業務自己,將日誌記錄,性能統計,安全控制,事務處理,異常處理等代碼從業務邏輯代碼中劃分出來,經過對這些行爲的分離,咱們但願能夠將它們獨立到非指導業務邏輯的方法中,進而改變這些行爲的時候不影響業務邏輯的代碼。spring
相關注解介紹以下:編程
@Aspect:做用是把當前類標識爲一個切面供容器讀取
@Pointcut:Pointcut是植入Advice的觸發條件。每一個Pointcut的定義包括2部分,一是表達式,二是方法簽名。方法簽名必須是 public及void型。能夠將Pointcut中的方法看做是一個被Advice引用的助記符,由於表達式不直觀,所以咱們能夠經過方法簽名的方式爲 此表達式命名。所以Pointcut中的方法只須要方法簽名,而不須要在方法體內編寫實際代碼。
@Around:環繞加強,至關於MethodInterceptor
@AfterReturning:後置加強,至關於AfterReturningAdvice,方法正常退出時執行
@Before:標識一個前置加強方法,至關於BeforeAdvice的功能,類似功能的還有
@AfterThrowing:異常拋出加強,至關於ThrowsAdvice
一:引入相關依賴api
二:Spring的配置文件 applicationContext.xml 中引入context、aop對應的命名空間;配置自動掃描的包,同時使切面類中相關方法中的註解生效,需自動地爲匹配到的方法所在的類生成代理對象。安全
三、建立簡單計算器的接口ArithmeticCalculator.java及實現類ArithmeticCalculatorImpl.javaapp
package com.svse.aop; public interface ArithmeticCalculator { //定義四個簡單的藉口 加減乘除算法 int add(int i, int j); int sub(int i, int j); int mul(int i, int j); int div(int i, int j); }
package com.svse.aop; import org.springframework.stereotype.Component; //將實現類加入Spring的IOC容器進行管理 @Component("arithmeticCalculator") public class ArithmeticCalculatorImpl implements ArithmeticCalculator { @Override public int add(int i, int j) { int result = i + j; return result; } @Override public int sub(int i, int j) { int result = i - j; return result; } @Override public int mul(int i, int j) { int result = i * j; return result; } @Override public int div(int i, int j) { int result = i / j; return result; } }
四、如今想在實現類中的每一個方法執行前、後、以及是否發生異常等信息打印出來,須要把日誌信息抽取出來,寫到對應的切面的類中 LoggingAspect.java 中
要想把一個類變成切面類,須要兩步,
① 在類上使用 @Component 註解 把切面類加入到IOC容器中
② 在類上使用 @Aspect 註解 使之成爲切面類
框架
package com.svse.aop; import java.util.Arrays; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; import com.tencentcloudapi.vod.v20180717.VodClient; /** * 日誌切面 * * @author zhaoshuiqing<br> * @date 2019年6月14日 下午3:03:29 */ @Component @Aspect public class LoggingAspect { //如今想在實現類中的每一個方法執行前、後、以及是否發生異常等信息打印出來,須要把日誌信息抽取出來,寫到對應的切面的類中 LoggingAspect.java 中 //要想把一個類變成切面類,須要兩步, //① 在類上使用 @Component 註解 把切面類加入到IOC容器中 //② 在類上使用 @Aspect 註解 使之成爲切面類 /** * 前置通知:目標方法執行以前執行如下方法體的內容 * @param jp */ @Before("execution(* com.svse.aop.*.*(..))") public void beforeMethod(JoinPoint jp){ String methodName =jp.getSignature().getName(); System.out.println("【前置通知】the method 【" + methodName + "】 begins with " + Arrays.asList(jp.getArgs())); } /** * 返回通知:目標方法正常執行完畢時執行如下代碼 * @param jp * @param result */ @AfterReturning(value="execution(* com.svse.aop.*.*(..))",returning="result") public void afterReturningMethod(JoinPoint jp, Object result){ String methodName =jp.getSignature().getName(); System.out.println("【返回通知】the method 【" + methodName + "】 ends with 【" + result + "】"); } /** * 後置通知:目標方法執行以後執行如下方法體的內容,無論是否發生異常。 * @param jp */ @After("execution(* com.svse.aop.*.*(..))") public void afterMethod(JoinPoint jp){ System.out.println("【後置通知】this is a afterMethod advice..."); } /** * 異常通知:目標方法發生異常的時候執行如下代碼 */ @AfterThrowing(value="execution(* com.qcc.beans.aop.*.*(..))",throwing="e") public void afterThorwingMethod(JoinPoint jp, NullPointerException e){ String methodName = jp.getSignature().getName(); System.out.println("【異常通知】the method 【" + methodName + "】 occurs exception: " + e); } /** * 環繞通知:目標方法執行先後分別執行一些代碼,發生異常的時候執行另一些代碼 * @return */ /*@Around(value="execution(* com.svse.aop.*.*(..))") public Object aroundMethod(ProceedingJoinPoint jp){ String methodName = jp.getSignature().getName(); Object result = null; try { System.out.println("【環繞通知中的--->前置通知】:the method 【" + methodName + "】 begins with " + Arrays.asList(jp.getArgs())); //執行目標方法 result = jp.proceed(); System.out.println("【環繞通知中的--->返回通知】:the method 【" + methodName + "】 ends with " + result); } catch (Throwable e) { System.out.println("【環繞通知中的--->異常通知】:the method 【" + methodName + "】 occurs exception " + e); } System.out.println("【環繞通知中的--->後置通知】:-----------------end.----------------------"); return result; }*/ }
五、編寫MainTest方法進行測試ide
package com.svse.aop; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MainTest { public static void main(String[] args) { //ClassPathXmlApplicationContext默認是加載src目錄下的xml文件 ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml"); ArithmeticCalculator arithmeticCalculator =(ArithmeticCalculator) ctx.getBean("arithmeticCalculator"); System.out.println(arithmeticCalculator.getClass()); int result = arithmeticCalculator.add(3, 5); System.out.println("result: " + result); result = arithmeticCalculator.div(5, 0); System.out.println("result: " + result); } }
運行結果:函數式編程
把其它代碼都註釋掉,把環繞通知的方法釋放出來,測試結果以下:
至此AOP註解面向切面記錄日誌就寫完了!