Spring Framework 之AOP
阿新 • • 發佈:2020-05-05
## Spring Framework 之AOP
[TOC]
## 問題
什麼是AOP?
AOP的好處是什麼?
AOP的實現方式有哪些?
Spring AOP 與 AspectJ AOP的區別?
## AOP概述
AOP(Aspect-Oriented Programming)翻譯為中文就是“面向切面程式設計”。從“面向過程”到“面向物件”程式設計,程式設計思想的發展永遠是朝更自然、更優雅地描述世界的方向發展。“面向切面“的出現也是這一目的,它是對面向物件的補充,使程式語言能夠更好地描述世界。
現實程式設計中無法將重複出現的程式碼抽取至父類中。例如鑑權模組、監控模組、日誌記錄模組等,我們無法將公共程式碼塊縱向抽取。那麼如何橫向抽取重複程式碼呢?AOP就能夠通過橫向抽取機制解決無法縱向抽取的問題,將分散在業務程式碼中的公共程式碼塊抽取至一個獨立的模組中。這也體現了設計模式中的“單一職責“的思想。
## AOP知識
#### 1、連線點(Joinpoint)
程式執行的某個特定位置。例如類初始化前後、函式呼叫前後、函式拋異常後等。類或程式碼塊具有邊界性質的特定點就成為“連線點”。
#### 2、切點(PointCut)
每個類或函式都可以認為是連線點,我們如何定位我們關注的“連線點“?我們不需要為每個類或函式新增Advice,PointCut就是通過規則為我們關注的joinpoint新增Advice。
#### 3、增強(Advice)
由aspect新增到特定的Join point的程式碼塊。
#### 4、目標物件(Target)
增強邏輯織入的目標類。
#### 5、引介(Introduction)
引介是一種特殊的增強,他為類新增一些屬性和方法。
例子:https://blog.csdn.net/u010599762/article/details/80182178
#### 6、織入(Weaving)
織入是將增強新增至目標類的具體連線點上的過程。
#### 7、代理(Proxy)
類被織入增強後就會產生新的結合了原類與增強的代理類。在Spring AOP中有兩種代理,分別是JDK動態代理和CGLib動態代理。
#### 8、切面(Aspect)
切面由切點和增強組成,包括增強的橫切邏輯和連線點。Spring AOP負責將切面中的增強邏輯織入指定的連線點中。
## 代理
### 靜態代理
##### 代理模式
代理模式提供對目標物件進行訪問方式,即通過代理物件訪問目標物件。可以在目標物件的基礎上增強,提供個性功能,達到擴招目標物件功能的作用。
**介面**
``` java
public interface Subject {
void request();
}
```
**具體實現**
```java
public class RealSubject implements Subject {
public void request() {
//業務邏輯
}
}
```
**代理類**
```java
public class Proxy implements Subject {
//要代理的實現類
private Subject subject = null;
public Proxy() {
this.subject = new Proxy();
}
//通過建構函式傳遞代理者
public Proxy(Object... objects) {
}
//實現介面定義的方法
public void request() {
this.before();
this.subject.request();
this.after();
}
//預處理
public void before() {
}
//後處理
public void after() {
}
}
```
### 動態代理
##### JDK動態代理
JDK動態代理設計到java.lang.relect包中的兩個類:Proxy和InvocationHandler,InvocationHandler可以通過實現該介面定義橫切邏輯,並通過反射機制呼叫目標類的程式碼,動態將橫切邏輯與業務邏輯編織到一起。Proxy利用InvocationHandler動態建立某一符合該介面的例項,生成目標類的代理物件。
```java
public class Monitor {
public static void begin(){
System.out.println("before");
}
public static void end(){
System.out.println("after");
}
}
```
```java
public interface CouponService {
void getCoupon();
}
```
```java
public class CouponServiceImpl implements CouponService {
public void getCoupon() {
//Monitor.begin();
try {
System.out.println("業務程式碼");
} catch (Exception e) {
throw new RuntimeException();
}
//Monitor.end();
}
}
```
```java
public class PerformanceHandler implements InvocationHandler {
//被代理物件
private Object target;
public PerformanceHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Monitor.begin();
Object object = method.invoke(target, args);
Monitor.end();
return object;
}
}
```
```java
public class Client {
public static void main(String[] args) {
//被代理物件
CouponService target = new CouponServiceImpl();
//讓PerformanceHandler將監視橫切邏輯編織到CouponService中
PerformanceHandler performanceHandler = new PerformanceHandler(target);
//通過Proxy的newProxyInstace()方法,為編織了業務邏輯與監控邏輯的handler建立一個符合CouponService介面的代理實現
CouponService proxy = (CouponService) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),performanceHandler);
proxy.getCoupon();
}
}
```
##### CGLIB動態代理
JDK建立代理只能為介面建立代理,實際開發中我們很難保證每個類都有其對應的介面,對於沒有通過介面定義業務方法的類,JDK已經沒法對其進行代理,這就出現了Cglib,通過位元組碼技術,為一個類建立子類,在子類中採用方法攔截的技術攔截所有父類方法的呼叫並織入橫切邏輯。
```java
public class CglibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz) {
//設定需要建立子類的類
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
//通過位元組碼技術動態建立子類例項
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before");
//通過代理類呼叫父類中的方法
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("after");
return result;
}
}
```
```java
public class Client {
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
//通過冬天生成子類的方式建立代理類
CouponServiceImpl couponService = (CouponServiceImpl) proxy.getProxy(CouponServiceImpl.class);
couponService.getCoupon();
}
}
```
### 靜態代理與動態代理區別
(1)靜態代理在編譯時就已經實現,編譯完成後代理類是一個實際的class檔案
(2)動態代理是在執行時動態生成的,即編譯完成後沒有實際的class檔案,而是在執行時動態生成類位元組碼,並載入到JVM中
### JDK動態代理 與CGLIB代理區別
Cglib所建立的動態代理,效能要比jdk建立的動態代理高。但對用Cglib建立代理的時間,JDK動態代理顯然要快很多。對於無需單例的代理物件或例項池可以使用CGLib來建立代理(無需頻繁建立),反之使用JDK動態代理。
## @AspectJ
AspectJ是語言級使用Java註解來AOP實現的一種方式,擴招Java語言,定義AOP語法,能夠在編譯期間提供橫切程式碼織入。
#### 切面定義
**Java Configuration方式配置**
```java
@Configuration
@EnableAspectJAutoProxy
public class AOPConfig {
}
```
**xml方式配置**