開始~~
動態代理在Java中有JDK動態代理和CGLIB動態代理,也就是在執行中動態生成位元組碼並載入到虛擬機器中供我們後續使用。下面我們先看下這兩種方式的區別。
兩者區別:
JDK動態代理:需要被代理物件的類實現了某些介面,生成的代理類也會實現相應的介面
CGLIB動態代理:不需要被代理物件的類實現了某些介面,生成的代理類為目標物件的類的子類
目標類和代理類關係
下面我們對JDK動態代理進行解析:(原始碼見下方)
1、java.lang.reflect.Proxy:根據InvocationHandler和目標類的類載入器,實現的介面生成代理類,生成的代理類也實現了這些介面,且使用相同的類載入器載入,然後再反射生成代理類的例項,反射生成的過程中會將InvocationHandler傳遞給父類的構造方法,因此例項化的代理類物件會持有InvocationHandler的引用
2、InvocationHandler:包含具體的被代理物件的引用,也程式碼中也就是target欄位,根據invoke方法執行相應的操作
3、最終生成的代理類會繼承Proxy類並實現HelloService介面
介面和實現類:
介面和實現類
生成的代理類:
final class $Proxy0 extends Proxy implements HelloService
先看下代理類生成的流程圖:步驟和生成的代理類的程式碼結構對應(原始碼如下)
透過配置系統屬性可以在本地磁碟生成代理類的Class檔案:(在原始碼中可以追蹤到對這個系統屬性值的判斷)
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
生成代理類的步驟和生成的代理類的程式碼結構的對應
InvocationHandler:
自定義MyInvocationHandler
測試:
測試
代理物件(proxyObject)的建立及hello方法執行流程圖:
1、例項化目標物件
2、根據目標物件構造MyInvocationHandler例項,
3、將MyInvocationHandler例項傳遞給代理類$Proxy0的構造方法來例項化代理類物件
proxyObject,在該過程中會初始化父類成員變數InvocationHandler型別的h欄位
public $Proxy0(InvocationHandler handler) throws { // 呼叫父類構造方法 // 將handler賦值給成員變數:InvocationHandler型別的h super(handler); }
4、因為生成的代理類也實現了介面HelloService,代理物件proxyObject可以強轉為HelloService型別。然後執行代理物件proxyObject的hello方法:
String returnValue = ((HelloService) proxyObject).hello();
解析:根據生成的代理類的程式碼來看,呼叫hello方法實際呼叫的是父類的h變數指向的物件的
invoke方法,也就是我們定義的MyInvocationHandler中的invoke方法,引數分別為當前代理物件proxyObject,當前執行的方法m3,引數陣列null
m3 = Class.forName("com.ww.jvm.dynamicproxy.HelloService").getMethod("hello");
public final String hello() { return (String)super.h.invoke(this, m3, (Object[])null);}
public MyInvocationHandler(Object target) { this.target = target;}@Overridepublic Object invoke( Object proxy, // 當前代理物件 Method method, // 當前執行的方法 Object[] args) throws Throwable { String methodName = method.getName(); System.err.println("before invoke " + methodName); Object returnValue = method.invoke(target, args); System.err.println("after invoke " + methodName); return returnValue;}
總結:JDK動態代理執行代理物件方法實際上是呼叫的是我們自定義的MyInvocationHandler中的invoke方法,而執行的具體是哪個方法都已經提前初始化到動態生成的代理類$Proxy0的欄位中了,也就是生成的代理類中的欄位m0、m1、m2、m3,這幾個欄位分別代表了Object類中的 hashCode方法、equals方法、toString方法和我們自己的介面中的方法hello。
Ps:CGLIB動態代理後續給出~~~
再次上圖,加深印象:
生成代理類的步驟和生成的代理類的程式碼結構的對應