Spring 覆盤 | AOP
Spring AOP 基礎 Java 動態代理實現,閱讀文章之前,你最好有以下基礎:
1、什麼是 AOP ?
AOP(Aspect Oriented Programming),即面向切面程式設計,它是 OOP(Object Oriented Programming,面向物件程式設計)的補充和完善。
在開發中,功能點通常分為橫向關注點和核心關注點,核心關注點就是業務關注的點,大部分是要給使用者看的。而橫向關注點是使用者不關心,而我們程式又必須實現的,它的特點是橫向分佈於核心關注點各處,比如日誌功能,核心關注點:增刪改查都需要實現日誌功能。如果用 面向物件程式設計來實現的話,那增刪改查都需要寫一遍日誌程式碼,這會造成非常多冗餘程式碼,顯然是不合理的。而此時,AOP 應運而生。它統一定義了,何時、何處執行這些橫向功能點
2、AOP 相關術語
要理解 AOP 首先要認識以下相關術語,有這麼個場景,我需要給使用者模組的增刪改查,實現日誌功能,我現在通過這個場景來解釋以上術語。
- 連線點(joinpoint)
被攔截到的點,因為 Spring 只支援方法型別的連線點,所以在 Spring 中連線點指的就是被攔截到的方法。場景中,連線點就是增刪改查方法本身。
- 通知(advice)
所謂通知指的就是指攔截到連線點之後要執行的程式碼,通知分為前置、後置、異常、最終、環繞通知五類。 1、前置通知(Before):在目標方法被呼叫之前呼叫通知功能; 2、後置通知(After):在目標方法完成之後呼叫通知,此時不會關 心方法的輸出是什麼; 3、返回通知(After-returning):在目標方法成功執行之後呼叫通 知; 4、異常通知(After-throwing):在目標方法丟擲異常後呼叫通知; 5、環繞通知(Around):通知包裹了被通知的方法,在被通知的方 法呼叫之前和呼叫之後執行自定義的行為。
- 切點(pointcut)
對連線點進行攔截的定義,它會匹配通知所要織入的一個或多個連線點。它的格式是這樣的:
- 切面(aspect)
類是對物體特徵的抽象,切面就是對橫切關注點的抽象,它定義了切點和通知。場景中,日誌功能就是這個抽象,它定義了你要對攔截方法做什麼?切面是通知和切點的結合。通知和切點共同定義了切面的全部內容——它是什麼,在何時和何處完成其功能。
- 織入(weave)
將切面應用到目標物件並導致代理物件建立的過程
- 引入(introduction)
在不修改程式碼的前提下,引入可以在執行期為類動態地新增一些方法或欄位
3、註解實現 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);
}
複製程式碼
定義一個實現類,程式碼如下:
@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,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[]){
// 1、建立 Spring 的 IOC 容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext_aop.xml");
// 2、從 IOC 容器中獲取 bean 例項
ArithmeticCalculator arithmeticCalculator = ctx.getBean(ArithmeticCalculator.class);
// 3、使用 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
複製程式碼
4、xml 實現 AOP
關於 xml 的實現方式,網上發現一篇文章寫的不錯,此處,不再贅述,有興趣的參考以下連結:
5、原始碼地址
推薦閱讀: