背景
最近像研究下spring 原理,看到代理這塊,想去看看JDK動態代理產生的過程,這裏想要知道最終生成代理類的結構,於是需要知道class 文件的結構。
解決辦法:
產生jdk 代理文件的方法
- 方法一:在調用測試代理的方法前加(此方法通知虛擬保存產生的代理文件):
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
運行後會在項目的更目錄下生成.class 文件,見:
- 方法二:將.class 二進制數據寫到本地文件中,然後用反編譯器(jd)打開就看到了。
調式Proxy 類是發現它調用
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
這個類的方法產生二進制數據,並且它還有一個 public static byte[] generateProxyClass(String var0, Class<?>[] var1) 靜態方法根據名稱和接口對應的Class 產生二進制數組,這個就是代理類對應的.class的二進制數據,如果將這些數據寫成二進制保存就好了,代碼如下:
public static void testProxyGenetate() {
byte[] newProxyClass = ProxyGenerator.generateProxyClass("$Proxy0", PersonServiceImpl.class.getInterfaces());
System.out.println(newProxyClass);
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(new File("/Users/shenjin/Desktop/$Proxy0.class"));
try {
fileOutputStream.write(newProxyClass);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileOutputStream != null) {
try {
fileOutputStream.flush();
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
這裏注意文件名$Proxy0.class 和 generateProxyClass 傳入的名稱參數要相同(我一開始傳的不同導致產生的文件無法打開),因爲java要求類名和類的文件名相同。
輸出cglib動態代理產生的類
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C:\\class");
產生的代理類結果
package com.leran;
import com.learn.proxy.PersonService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class proxy01 extends Proxy implements PersonService {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m4;
private static Method m0;
public proxy01(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final boolean saveUser(String var1) throws {
try {
return (Boolean)super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String getUserName() throws {
try {
return (String)super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.learn.proxy.PersonService").getMethod("saveUser", Class.forName("java.lang.String"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m4 = Class.forName("com.learn.proxy.PersonService").getMethod("getUserName");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
結論
jdk 代理並不神祕,基本原理就是:
- 獲取需要代理的接口的Class 對象信息
- 生成一個新的.class文件,這個文件就是根據.class文件的生成規則動態的產生一個基於二進制格式的數組,數組本質就是.class 的二進制數組,它繼承自Proxy類實現了代理接口,並且加入了通用的HashCode方法,toString,equals三個方法。
- Proxy 調用defineClass0 這個native方法基於二進制數組產生一個Class 對象的實例,即代理對象的Class.
- 調用cl.getConstructor(constructorParams).newInstance(new Object[]{h});返回代理類。
- 對代理類方法的調用都會調用super.h.invoke(this, m4, (Object[])null); 因此可以在Invoke方法中進行各種攔截操作。