AOP
一、AOP概述
1. 什麼是AOP
- AOP(Aspect Oriented Programing) 面向切面編程
- AOP 採取橫向抽取機制,取代了傳統的繼承縱向繼承體系重複性代碼(性能監視、事務管理、安全檢查、緩存)
- Spring 的AOP 採用了純Java 實現,不需要專門的編譯過程和類加載器,在運行期間通過動態代理的方式向目標類注入增強代碼。
2. AOP的應用場景
- 在不修改源代碼的情況下對程序進行增強
- 權限校驗、日誌記錄、性能監控、事務控制
3. AOP的底層實現
代理機制
Spring的AOP的底層用到兩種代理機制
- JDK的動態代理 針對實現了接口的類產生代理
- CGLIB的動態代理 針對沒有實現接口的類產生代理
二、什麼是代理模式
1. 代理模式的概述
代理(Proxy)是一種設計模式,提供了對目標對象另外的訪問方式;即通過代理對象訪問目標對象,這樣做的好處是:可以在目標對象實現的基礎上,增強額外的功能操作,即擴展目標對象的功能。
這裏使用到編程種的一個思想:不要隨意去修改別人已經寫好的代碼或者方法,可以通過代理的方式來擴展該方法。(對修改關閉,對擴展開放)
2. 代理模式的分類
- 靜態代理
- 動態代理
三、靜態代理
靜態代理在使用時,需要定義接口或者父類,被代理對象與代理對象一起實現相同的接口或者是繼承相同父類。
1. 實現方法
先把項目結構放出來
1). 使用繼承關係實現靜態代理
- 創建Person
- 創建SuperPerson
- 測試
2). 使用裝飾者模式實現靜態代理
- 創建SuperPerson2
- 測試
3). 靜態代理實例(對數據持久層進行事務管理)
- 創建UserDao
- 創建UserDaoImpl
- 創建UserDaoProxy
- 測試
2. 靜態代理總結
- 可以做到在不修改目標對象的功能前提下,對目標功能進行擴展
- 因爲代理對象需要與目標對象實現一樣的接口,所以會有很多代理類,類太多。同時,一旦接口增加方法,目標對象與代理對象都要維護。
- 解決靜態代理種的缺點:使用動態代理方式。
4. 動態代理
1. JDK動態代理
- 項目結構圖
- 創建UserDao
- 創建UserDaoImpl
- 創建ProxyFactory
package com.lasing.dao;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 代理工廠
* 作用:創建代理類和代理對象
* */
public class ProxyFactory{
//聲明目標對象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
/**
* 得到代理對象
* @return
* */
public Object getProxyInstance() {
/**
* 參數說明
* ClassLoader loader, 類加載器
* Class<?>[] interfaces, 目標類所實現的所有接口數組
* InvocationHandle h 當代理對象被創建之後,調用目標對象的方法時觸發的方法回調
**/
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),
new InvocationHandler() {
/**
* proxy:代理對象
* method:用戶要調用的目標對象的方法
* args:目標對象的方法的參數
* */
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//前置增強
begin();
//執行目標對象的方法
Object invoke = method.invoke(target, args);
//後置增強
end();
return invoke;
}
});
}
public void end() {
System.out.println("提交事務");
System.out.println("關閉事務");
}
public void begin() {
System.out.println("建立連接");
System.out.println("開啓事務");
}
}
- 測試
- JDK動態代理總結
優點:
|-- 有了目標對象,只要通過代理工廠就可以爲所有的目標對象去創建代理對象不用顯示創建代理類了
缺點:
|-- 目標對象必須至少實現一個接口;
|-- 代理對象強轉時只能轉成目標對象所實現的接口類型
原理:
|-- 當調用Proxy.newProxtInstance 方法時,系統會在內存裏面根據代理對象的類加載器和目標對象所實現的所有接口去內存裏面創建一個實現了目標對象所有接口的代理類,並使用這個代理類創建一個代理對象返回給調用者
2. CGLIB動態代理
上面的靜態代理和動態代理都要要求目標對象是實現一個接口的目標對象,但是有時候目標對象只是一個單獨的對象,並沒有實現任何的接口,這個時候就可以使用目標對象子類的方式類實現,這種方法就叫做CGLIB代理。
CGLIB代理,也叫作子類代理,它是在內存種構建一個子類對象從而實現目標對象功能的擴展。
JDK 的動態代理有一個限制,就是使用動態代理的對象必須實現一個或多個接口,如果想目標沒有實現接口的類,就可以使用CGLIB實現。
CGLIB 是一個強大的高性能的代碼生成包,它可以在運行期間擴展java 類與實現Java 接口,它廣泛的被許多AOP 的框架使用,例如Spring AOP 和synaop,爲它們提供方法的interception(攔截)
CGLIB包的底層是通過使用一個小而快的字節碼處理框架ASM來轉換字節碼並生成新的類,不鼓勵直接使用ASM,因爲它要求你必須對JVM內部結構包括class 文件的格式和指令集都很熟悉。
CGLIB子類代理實現方法:
1). 引入CGLIB 的jar 文件,但是Spring 的核心包中包括了CGLIB 功能,所以直接引入spring-core-5.0.2.RELEASE.jar 即可。
2). 引入功能包後,就可以在內存中動態構建子類
3). 代理的類不能爲final,否則報錯
4). 目標對象的方法如果爲final/static,那麼就不會攔截,既不會執行目標對象額外的業務方法
- 項目結構圖
- 創建目標類
- 創建代理工廠
package com.lasing.dao;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* 代理工廠
* 作用:創建代理類和代理對象
* */
public class ProxyFactory implements MethodInterceptor{
//聲明目標對象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
/**
* 得到代理對象
* @return
* */
public Object getProxyInstance() {
//1,創建一個子類對象的構造器
Enhancer enhancer = new Enhancer();
//2,設置父類
enhancer.setSuperclass(target.getClass());
//3,設置回調 就是當前對象
enhancer.setCallback(this);
//4,在內存裏面生成代理對象
return enhancer.create();
}
/**
* 攔截方法
* obj:目標對象
* method:目標對象所調用的方法
* args:方法參數
* proxy:方法代理
* */
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
begin();
method.invoke(target, args);
end();
return null;
}
public void end() {
System.out.println("提交事務");
System.out.println("關閉事務");
}
public void begin() {
System.out.println("建立連接");
System.out.println("開啓事務");
}
}
- 測試
- 總結
在spring的AOP編程中
|-- 如果加入容器的目標對象有實現接口,用JDK的動態代理
|-- 如果目標對象沒有實現接口,用CGLIB代理