JDK動態代理實現原理詳解(原始碼分析)
無論是靜態代理,還是Cglib動態代理,都比較容易理解,本文就通過進入原始碼的方式來看看JDK動態代理的實現原理進行分析
要了解動態代理的可以參考另一篇文章,有詳細介紹,這裡僅僅對JDK動態代理做原始碼分析。
一文理解JDK靜態代理、JDK動態代理、Cglib動態代理
原始碼分析共識
為了避免影響分析,一些沒用的程式碼已經被清理
JDK動態代理原始碼分析
平時我們是這樣使用動態代理的,主要方法在Proxy.newProxyInstance(classLoader, userService.getClass().getInterfaces(), proxyHandler)
public static void main(String[] args) {
// 儲存建立的代理類,預設儲存專案根目錄下的com.sun.proxy.$Proxy0.class
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
// 代理的物件,可以動態切換代理
RealSubject userService = new RealSubject();
// 代理的具體實現
ProxyHandler proxyHandler = new ProxyHandler(userService);
ClassLoader classLoader = proxyHandler.getClass().getClassLoader();
// note 根據指定引數動態建立代理物件
Subject proxyObject = (Subject) Proxy.newProxyInstance(classLoader,
userService.getClass().getInterfaces(), proxyHandler);
proxyObject.visit();
}
setup 1:進入Proxy.newProxyInstance
方法
// 返回指定介面的代理類的例項,該介面將方法呼叫分派給指定的呼叫處理程式
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException {
Objects.requireNonNull(h);
// 拷貝一份所有業務實現的介面
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
//【代理類生成的主要方法】查詢在(如果快取中有則在快取獲取)或生成指定代理類的class代理物件
Class<?> cl = getProxyClass0(loader, intfs);
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
// 獲取代理類的建構函式,constructorParams為構造引數
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
// 設定構造可見性
cons.setAccessible(true);
return null;
}
});
}
// 【重點】使用建構函式例項化代理物件,入參就是我們一定要實現的InvocationHandler類
// 建立的代理類中會生成具有相同介面的物件,然後呼叫代理類方法的時候轉發到InvocationHandler的invoke方法
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException | InstantiationException e) {
...
} catch (InvocationTargetException e) {
...
} catch (NoSuchMethodException e) {
...
}
}
可以看到,主要是最後的返回值getProxyClass0(loader, intfs)
,我們進入詳細看下
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
// 代理介面數限制
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// 獲取代理類有兩種方式:
// 1.給定的類載入器loader實現給定的介面定義interfaces的代理類存在,返回快取
// 2.不存在通過ProxyClassFactory建立代理類
return proxyClassCache.get(loader, interfaces);
}
這裡實際上是一個快取物件,如果有,則直接在快取中返回,如果沒有則通過ProxyClassFactory
建立代理類,看下這個proxyClassCache
欄位的定義
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
ProxyClassFactory是Proxy的一個靜態私有類
// 根據類載入器和介面列表生成、定義和返回一個代理類。
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
// 所有代理類的命名字首
private static final String proxyClassNamePrefix = "$Proxy";
// 使用一個唯一的number作為proxy class標識
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
// 確認類載入器解析了這個名字的介面到相同的class物件
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
// 確認代理的Class是介面,這也是JDK動態代理的劣勢
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
// 校驗介面是否有重複
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // 定義proxy class所在的package
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
// 驗證所有non-public的proxy interfaces都在同一個package
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
// 如果沒有non-public的interfaces,預設使用包都為com.sun.proxy
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* 選擇要生成的代理類的名稱
* 大致為:com.sun.proxy.$Proxy1
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
// 生成特殊的proxy class
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
// 把代理類載入到JVM中,至此動態代理過程基本結束了
return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
...
}
}
}
這裡是確定了代理類的名稱,然後使用ProxyGenerator.generateProxyClass()
方法生成代理類的位元組流,接下來我們看看這個方法的內部實現
sun.misc.ProxyGenerator在sun包之下,沒有開源,可以通過反編譯的形式獲取;或者使用openJDK中的原始碼
// 生成一個代理類檔案並返回位元組流
private byte[] generateClassFile() {
// 接下去的步驟基本都是構建一個class檔案
// Step 1: 新增從Object基類中繼承的方法
addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);
// 新增介面中的方法實現
for (Class<?> intf : interfaces) {
for (Method m : intf.getMethods()) {
addProxyMethod(m, intf);
}
}
// 對每一個方法驗證返回型別
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
checkReturnTypes(sigmethods);
}
try {
// Step 2: 新增構造方法,入參為InvocationHandler
methods.add(generateConstructor());
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
for (ProxyMethod pm : sigmethods) {
// 新增靜態欄位,大致為:private static Method m1;
fields.add(new FieldInfo(pm.methodFieldName,
"Ljava/lang/reflect/Method;",
ACC_PRIVATE | ACC_STATIC));
// 生成代理方法的MethodInfo並新增到methods
methods.add(pm.generateMethod());
}
}
// 新增靜態初始化程式碼塊,結果大致如下:
// static {
// try {
// m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
// m2 = Class.forName("java.lang.Object").getMethod("toString");
// m3 = Class.forName("com.siqi.proxy.jdk_dynamic_proxy.Subject").getMethod("visit");
// m0 = Class.forName("java.lang.Object").getMethod("hashCode");
// } catch (NoSuchMethodException var2) {
// throw new NoSuchMethodError(var2.getMessage());
// } catch (ClassNotFoundException var3) {
// throw new NoClassDefFoundError(var3.getMessage());
// }
// }
methods.add(generateStaticInitializer());
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception", e);
}
if (methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
}
if (fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
}
cp.getClass(dotToSlash(className));
cp.getClass(superclassName);
for (Class<?> intf: interfaces) {
cp.getClass(dotToSlash(intf.getName()));
}
cp.setReadOnly();
// Step 3: 生成最終檔案
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);
try {
dout.writeInt(0xCAFEBABE);
dout.writeShort(CLASSFILE_MINOR_VERSION);
dout.writeShort(CLASSFILE_MAJOR_VERSION);
cp.write(dout);
dout.writeShort(accessFlags);
dout.writeShort(cp.getClass(dotToSlash(className)));
dout.writeShort(cp.getClass(superclassName));
dout.writeShort(interfaces.length);
for (Class<?> intf : interfaces) {
dout.writeShort(cp.getClass(
dotToSlash(intf.getName())));
}
dout.writeShort(fields.size());
for (FieldInfo f : fields) {
f.write(dout);
}
dout.writeShort(methods.size());
for (MethodInfo m : methods) {
m.write(dout);
}
dout.writeShort(0);
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception", e);
}
// 返回建立的class檔案位元組流
return bout.toByteArray();
}
至此原始碼基本就分析完成了,接下來我們看看最終生成的class檔案:
package com.sun.proxy;
import com.siqi.proxy.jdk_dynamic_proxy.Subject;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements Subject {
// 生成的靜態方法欄位
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
// 代理了equals
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);
}
}
// 打理了toString
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);
}
}
// 這裡就是我們的業務介面visit
public final void visit() throws {
try {
// 【重要】這句話就是呼叫了InvocationHandler物件的invoke
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
// 代理了hashCode
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);
}
}
// 使用靜態程式碼塊初始化所有Method欄位
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.siqi.proxy.jdk_dynamic_proxy.Subject").getMethod("visit");
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動態代理中,主要用到三個類:Proxy
、私有內部類ProxyClassFactory
、代理生成類ProxyGenerator
,通過對介面的clone一份,實現一個擁有同樣介面的代理類,呼叫的時候再轉發到IvocationHandler的類中,這個類中持有對RealSubject的引用,在invoke方法實現代理,對實際方法進行呼叫super.h.invoke(this, m3, (Object[])null);
相關文章
- Java JDK 動態代理使用及實現原理分析JavaJDK
- Spring原始碼剖析5:JDK和cglib動態代理原理詳解Spring原始碼JDKCGLib
- Java JDK 動態代理(AOP)使用及實現原理分析JavaJDK
- JDK動態代理詳解JDK
- Java-JDK動態代理(AOP)使用及實現原理分析JavaJDK
- 細說JDK動態代理的實現原理JDK
- 基於JDK的動態代理原理分析JDK
- 【乾貨】JDK動態代理的實現原理以及如何手寫一個JDK動態代理JDK
- 看過Jdk動態代理類長啥樣嗎?Jdk動態代理原理原始碼一本到JDK原始碼
- Spring的JDK動態代理如何實現的(原始碼解析)SpringJDK原始碼
- 代理模式詳解:靜態代理、JDK動態代理與Cglib動態代理模式JDKCGLib
- 靜態代理和動態代理(jdk/cglib)詳解JDKCGLib
- 設計模式【3.2】-- JDK動態代理原始碼分析有多香?設計模式JDK原始碼
- 深挖JDK動態代理(二):JDK動態生成後的位元組碼分析JDK
- 純手寫實現JDK動態代理JDK
- 【深度思考】聊聊JDK動態代理原理JDK
- spring aop原理 JDK動態代理和CGLIB動態代理SpringJDKCGLib
- 小豹子帶你看原始碼:JDK 動態代理原始碼JDK
- 最詳細的代理講解--JDK動態代理和cglib代理JDKCGLib
- java動態代理基本原理及proxy原始碼分析一Java原始碼
- Spring框架系列(12) - Spring AOP實現原理詳解之JDK代理實現Spring框架JDK
- JDK解構 - Java中的引用和動態代理的實現JDKJava
- Java 動態代理原理圖解 (附:2種實現方式詳細對比)Java圖解
- JDK動態代理JDK
- CGLib動態代理原理及實現CGLib
- RPC核心實現原理-動態代理RPC
- [動態代理三部曲:下] - 從動態代理,看Retrofit的原始碼實現原始碼
- jdk proxy invocationhandler (jdk動態代理)JDK
- JDK 和 CGLib 實現動態代理和區別JDKCGLib
- Java代理(jdk靜態代理、動態代理和cglib動態代理)JavaJDKCGLib
- JDK動態代理初探JDK
- 【趣味設計模式系列】之【代理模式2--JDK動態代理原始碼解析】設計模式JDK原始碼
- 【Spring AOP】AOP 底層實現原理 —— 動態代理類的建立(JDK、CGlib)、工廠如何加工原始物件SpringJDKCGLib物件
- Spring原始碼剖析7:AOP實現原理詳解Spring原始碼
- HashMap 實現原理與原始碼分析HashMap原始碼
- HashMap實現原理及原始碼分析HashMap原始碼
- Java動態代理 jdk和cglib的實現比較JavaJDKCGLib
- JDK動態代理和 CGLIB 代理JDKCGLib