Spring AOP 基礎 Java 動態代理實現,閱讀文章以前,你最好有如下基礎:html
java動態代理java
AOP(Aspect Oriented Programming),即面向切面編程,它是 OOP(Object Oriented Programming,面向對象編程)的補充和完善。git
在開發中,功能點一般分爲橫向關注點和核心關注點,核心關注點就是業務關注的點,大部分是要給用戶看的。而橫向關注點是用戶不關心,而咱們程序又必須實現的,它的特色是橫向分佈於核心關注點各處,好比日誌功能,核心關注點:增刪改查都須要實現日誌功能。若是用 面向對象編程來實現的話,那增刪改查都須要寫一遍日誌代碼,這會形成很是多冗餘代碼,顯然是不合理的。而此時,AOP 應運而生。它統必定義了,什麼時候、何處執行這些橫向功能點github
要理解 AOP 首先要認識如下相關術語,有這麼個場景,我須要給用戶模塊的增刪改查,實現日誌功能,我如今經過這個場景來解釋以上術語。spring
被攔截到的點,由於 Spring 只支持方法類型的鏈接點,因此在 Spring 中鏈接點指的就是被攔截到的方法。場景中,鏈接點就是增刪改查方法自己。編程
所謂通知指的就是指攔截到鏈接點以後要執行的代碼,通知分爲前置、後置、異常、最終、環繞通知五類。
一、前置通知(Before):在目標方法被調用以前調用通知功能;
二、後置通知(After):在目標方法完成以後調用通知,此時不會關
心方法的輸出是什麼;
三、返回通知(After-returning):在目標方法成功執行以後調用通
知;
四、異常通知(After-throwing):在目標方法拋出異常後調用通知;
五、環繞通知(Around):通知包裹了被通知的方法,在被通知的方
法調用以前和調用以後執行自定義的行爲。app
對鏈接點進行攔截的定義,它會匹配通知所要織入的一個或多個鏈接點。它的格式是這樣的:測試
類是對物體特徵的抽象,切面就是對橫切關注點的抽象,它定義了切點和通知。場景中,日誌功能就是這個抽象,它定義了你要對攔截方法作什麼?切面是通知和切點的結合。通知和切點共同定義了切面的所有內容——它是什麼,在什麼時候和何處完成其功能。ui
將切面應用到目標對象並致使代理對象建立的過程spa
在不修改代碼的前提下,引入能夠在運行期爲類動態地添加一些方法或字段
首先,定義一個加減乘除的接口,代碼以下:
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); }
定義一個實現類,代碼以下:
@Component("arithmeticCalculator") public class ArithmeticCalculatorImpl implements ArithmeticCalculator { public int add(int i, int j) { int result = i + j; return result; } public int sub(int i, int j) { int result = i - j; return result; } public int mul(int i, int j) { int result = i * j; return result; } public int div(int i, int j) { int result = i / j; return result; } }
定義切面,代碼以下:
/** * 1. 加入 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 * spring-aspects-4.0.0.RELEASE.jar * * 2. 在 Spring 的配置文件中加入 aop 的命名空間。 * * 3. 基於註解的方式來使用 AOP * 3.1 在配置文件中配置自動掃描的包: <context:component-scan base-package="com.atguigu.spring.aop"></context:component-scan> * 3.2 加入使 AspjectJ 註解起做用的配置: <aop:aspectj-autoproxy></aop:aspectj-autoproxy> * 爲匹配的類自動生成動態代理對象. * * 4. 編寫切面類: * 4.1 一個通常的 Java 類 * 4.2 在其中添加要額外實現的功能. * * 5. 配置切面 * 5.1 切面必須是 IOC 中的 bean: 實際添加了 @Component 註解 * 5.2 聲明是一個切面: 添加 @Aspect * 5.3 聲明通知: 即額外加入功能對應的方法. * 5.3.1 前置通知: @Before("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(int, int))") * @Before 表示在目標方法執行以前執行 @Before 標記的方法的方法體. * @Before 裏面的是切入點表達式: * * 6. 在通知中訪問鏈接細節: 能夠在通知方法中添加 JoinPoint 類型的參數, 從中能夠訪問到方法的簽名和方法的參數. * * 7. @After 表示後置通知: 在方法執行以後執行的代碼. */ //經過添加 @EnableAspectJAutoProxy 註解聲明一個 bean 是一個切面! @Component @Aspect public class LoggingAspect { /** * 在方法正常開始前執行的代碼 * @param joinPoint */ @Before("execution(public int com.nasus.spring.aop.impl.*.*(int, int))") public void beforeMethod(JoinPoint joinPoint){ String methodName = joinPoint.getSignature().getName(); Object [] args = joinPoint.getArgs(); System.out.println("The method " + methodName + " begins with " + Arrays.asList(args)); } /** * 在方法執行後執行的代碼,不管方法是否拋出異常 * @param joinPoint */ @After("execution(* com.nasus.spring.aop.impl.*.*(..))") public void afterMethod(JoinPoint joinPoint){ String methodName = joinPoint.getSignature().getName(); System.out.println("The method " + methodName + " ends"); } /** * 在方法正常結束後執行的代碼 * 返回通知是能夠訪問到方法的返回值的 * @param joinPoint * @param result */ @AfterReturning(value = "execution(public int com.nasus.spring.aop.impl.*.*(int, int))", returning = "result") public void afterReturning(JoinPoint joinPoint, Object result){ String methodName = joinPoint.getSignature().getName(); System.out.println("The method " + methodName + " ends with " + result); } /** * 在目標方法出現異常時,會執行的代碼 * 能夠訪問到異常對象,能夠指定在出現特定異常時再執行通知代碼 * @param joinPoint * @param ex */ @AfterThrowing(value = "execution(public int com.nasus.spring.aop.impl.*.*(int, int))", throwing = "ex") public void afterThrowing(JoinPoint joinPoint, Exception ex){ String methodNames = joinPoint.getSignature().getName(); System.out.println("The method " + methodNames + " occurs exception: " + ex); } /** * 環繞通知須要攜帶 ProceedingJoinPoint 類型參數 * 環繞通知相似於動態代理的全過程; ProceedingJoinPoint 類型的參數能夠決定是否執行目標方法 * 且環繞通知必須有返回值,返回值極爲目標方法的返回值 * @param pjd * @return */ @Around("execution(public int com.nasus.spring.aop.impl.*.*(int, int))") public Object aroundMethod(ProceedingJoinPoint pjd){ Object result = null; String methodName = pjd.getSignature().getName(); try { // 前置通知 System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs())); // 執行目標方法 result = pjd.proceed(); // 返回通知 System.out.println("The method " + methodName + " ends with " + result); }catch (Throwable e) { // 異常通知 System.out.println("The method " + methodName + " occurs exception: " + e); throw new RuntimeException(e); } // 後置通知 System.out.println("The method " + methodName + " ends"); return result; } }
xml 配置,代碼以下:
<?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" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!-- 自動掃描的包 --> <context:component-scan base-package="com.nasus.spring.aop.impl"></context:component-scan> <!-- 使 AspectJ 的註解起做用 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> <
測試方法:
public class Main { public static void main(String args[]){ // 一、建立 Spring 的 IOC 容器 ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext_aop.xml"); // 二、從 IOC 容器中獲取 bean 實例 ArithmeticCalculator arithmeticCalculator = ctx.getBean(ArithmeticCalculator.class); // 三、使用 bean arithmeticCalculator.add(3,6); } }
測試結果:
The method add begins with [3, 6] The method add begins with [3, 6] The method add ends with 9 The method add ends The method add ends The method add ends with 9
關於 xml 的實現方式,網上發現一篇文章寫的不錯,此處,再也不贅述,有興趣的參考如下連接:
http://www.javashuo.com/article/p-rdagsfro-db.html
https://github.com/turoDog/review_spring
推薦閱讀: