一、概念:
Java動態代理的優勢是實現無侵入式的代碼擴展,也就是方法的增強;讓你可以在不用修改源碼的情況下,增強一些方法;在方法的前後你可以做你任何想做的事情(如Spring AOP、cglib等
)。
代理模式:
爲其他對象提供一種代理以控制對這個對象的訪問。在某些情況下,一個對象不適合或者不能直接引用另一個對象,而代理對象可以在客戶端和目標對象之間起到中介的作用。(個人理解就是對調用目標的一種封裝
)
靜態代理
由程序員創建或者由第三方工具生成,再進行編譯;在程序運行之前,代理類的.class文件已經存在了。
(靜態代理類通常只代理一個類,靜態代理事先知道要代理的是什麼。
)
動態代理:
在程序運行時,通過反射機制動態生成。動態代理類通常代理接口下的所有類。( 動態代理事先不知道要代理的是什麼,只有在運行的時候才能確定。
) 動態代理的調用處理程序必須事先InvocationHandler接口,及使用Proxy類中的newProxyInstance方法動態的創建代理類。JDK動態代理只能代理接口,要代理類需要使用第三方的CLIGB等類庫。
二、JDK動態代理
上面的 JDK Proxy 例子,非常簡單地實現了動態代理的構建和代理操作。
首先,實現對應的 InvocationHandler;
然後,以接口 Hello 爲紐帶,爲被調用目標構建代理對象;
進而應用程序就可以使用代理對象間接運行調用目標的邏輯。
代理爲應用插入額外邏輯(System.out.println("invoke:")
)提供了便利的入口。
從 API 設計和實現的角度,這種實現仍然有侷限性,因爲它是以接口爲中心的,相當於添加了一種對於被調用者沒有太大意義的限制。我們實例化的是 Proxy
對象,而不是真正的被調用類型,這在實踐中還是可能帶來各種不便和能力退化。
如果被調用者沒有實現接口,而我們還是希望利用動態代理機制,那麼可以考慮其他方式(如cglib
)。
三、cglib動態代理
cglib
動態代理採取的是創建目標類的子類的方式,因爲是子類化,我們可以達到近似使用被調用者本身的效果,克服了接口的依賴。
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author wangcw
* @create 2020-01-19 17:05
* @description:Cglib動態代理
**/
public class DynamicProxyDemo {
public static void main(String[] args) {
HelloCglib helloCglib = new HelloCglib();
Hello helloProxy = (Hello) helloCglib.getInstance(new Hello());
helloProxy.sayHello();
}
/* 目標類 */
static class Hello {
public void sayHello() {
System.out.println("hello world");
}
}
static class HelloCglib implements MethodInterceptor {
private Object target;
public Object getInstance(Object target){
this.target = target;
Enhancer enhancer = new Enhancer();
//設置父類爲目標類
enhancer.setSuperclass(this.target.getClass());
// 回調方法
enhancer.setCallback(this);
// 創建代理對象
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("擴展邏輯 1 - before invoke:");//擴展邏輯
methodProxy.invokeSuper(o, objects);
System.out.println("擴展邏輯 2 - after invoke!");//擴展邏輯
return null;
}
}
}
四、優勢比較和應用
JDK Proxy 的優勢:
1、最小化依賴關係,減少依賴意味着簡化開發和維護,JDK 本身的支持,可能比 cglib 更加可靠;
2、平滑進行 JDK 版本升級,而字節碼類庫通常需要進行更新以保證在新版 Java 上能夠使用;
3、代碼實現簡單;
4、新版本JDK也使用ASM提高性能。
基於類似 cglib 框架的優勢:
1、有的時候調用目標可能不便實現額外接口,從某種角度看,限定調用者實現接口是有些侵入性的實踐,類似 cglib 動態代理就沒有這種限制;
2、只操作我們關心的類,而不必爲其他相關類增加工作量;
3、基於ASM字節碼,高性能;
4、通過生成業務類的子類作爲代理類。
動態代理應用非常廣泛,雖然最初多是因爲 RPC 等使用進入我們視線,但是動態代理的使用場景遠遠不僅如此,它完美符合 Spring AOP 等切面編程。簡單來說它可以看作是對 OOP 的一個補充,因爲 OOP 對於跨越不同對象或類的分散、糾纏邏輯表現力不夠,比如在不同模塊的特定階段做一些事情,類似日誌、用戶鑑權、全局性異常處理、性能監控,甚至事務處理等,你可以參考下面這張圖。