JDK動態代理實現原理(轉)
轉:https://www.cnblogs.com/zuidongfeng/p/8735241.html
JDK動態代理
JDK動態代理是代理模式的一種實現方式,其只能代理介面。
使用步驟
1、 新建一個介面
2、 為介面建立一個實現類
3、 建立代理類實現java.lang.reflect.InvocationHandler介面
4、 測試
簡單案例
根據使用步驟:
首先新建一個介面Subject
package com.lnjecit.proxy; /** * Subject * 抽象主題介面 * @author * @create 2018-03-29 14:16 **/ public interface Subject { void doSomething(); }
然後為介面RealSubject新建一個實現類RealSubject
/** * RealSubject * 真實主題類 * @author * @create 2018-03-29 14:21 **/ public class RealSubject implements Subject { @Override public void doSomething() { System.out.println("RealSubject do something"); } }
接著建立一個代理類JDKDynamicProxy實現java.lang.reflect.InvocationHandler介面,重寫invoke方法
package com.lnjecit.proxy.dynamic.jdk; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * JDKDynamicProxy * jdkd動態代理 * * @author * @create 2018-03-29 16:17 **/ public class JDKDynamicProxy implements InvocationHandler { private Object target; public JDKDynamicProxy(Object target) { this.target = target; } /** * 獲取被代理介面例項物件 * @param <T> * @return */ public <T> T getProxy() { return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Do something before"); Object result = method.invoke(target, args); System.out.println("Do something after"); return result; } }
新建測試類Client測試結果
package com.lnjecit.proxy; import com.lnjecit.proxy.dynamic.jdk.JDKDynamicProxy; /** * Client * client測試程式碼 * * @author * @create 2018-03-29 14:26 **/ public class Client { public static void main(String[] args) { // 儲存生成的代理類的位元組碼檔案 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); // jdk動態代理測試 Subject subject = new JDKDynamicProxy(new RealSubject()).getProxy(); subject.doSomething(); } }
輸出結果:
Do something before
RealSubject do something
Do something after
由於設定
sun.misc.ProxyGenerator.saveGeneratedFiles 的值為true,所以代理類的位元組碼內容儲存在了專案根目錄下,檔名為$Proxy0.class
原始碼分析
這裡檢視JDK1.8.0_65的原始碼,通過debug學習JDK動態代理的實現原理
大概流程
1、為介面建立代理類的位元組碼檔案
2、使用ClassLoader將位元組碼檔案載入到JVM
3、建立代理類例項物件,執行物件的目標方法
動態代理涉及到的主要類:
java.lang.reflect.Proxy
java.lang.reflect.InvocationHandler
java.lang.reflect.WeakCache
sun.misc.ProxyGenerator
首先看Proxy類中的newProxyInstance方法:
@CallerSensitive public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { // 判斷InvocationHandler是否為空,若為空,丟擲空指標異常 Objects.requireNonNull(h); final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * 生成介面的代理類的位元組碼檔案 */ Class<?> cl = getProxyClass0(loader, intfs); /* * 使用自定義的InvocationHandler作為引數,呼叫建構函式獲取代理類物件例項 */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } 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; } }); } return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } }
newProxyInstance方法呼叫getProxyClass0方法生成代理類的位元組碼檔案。
private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { // 限定代理的介面不能超過65535個 if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } // 如果快取中已經存在相應介面的代理類,直接返回;否則,使用ProxyClassFactory建立代理類 return proxyClassCache.get(loader, interfaces); }
其中快取使用的是WeakCache實現的,此處主要關注使用ProxyClassFactory建立代理的情況。ProxyClassFactory是Proxy類的靜態內部類,實現了BiFunction介面,實現了BiFunction介面中的apply方法。
當WeakCache中沒有快取相應介面的代理類,則會呼叫ProxyClassFactory類的apply方法來建立代理類。
private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> { // 代理類字首 private static final String proxyClassNamePrefix = "$Proxy"; // 生成代理類名稱的計數器 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<?> 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"); } /* * 校驗該類是否是介面型別 */ 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; // 代理類包名 int accessFlags = Modifier.PUBLIC | Modifier.FINAL; /* * 非public介面,代理類的包名與介面的包名相同 */ 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) { // public代理介面,使用com.sun.proxy包名 proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } /* * 為代理類生成名字 */ long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num; /* * 真正生成代理類的位元組碼檔案的地方 */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { // 使用類載入器將代理類的位元組碼檔案載入到JVM中 return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { throw new IllegalArgumentException(e.toString()); } } }
,
在ProxyClassFactory類的apply方法中可看出真正生成代理類位元組碼的地方是ProxyGenerator類中的generateProxyClass,該類未開源,但是可以使用IDEA、或者反編譯工具jd-gui來檢視。
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) { ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2); final byte[] var4 = var3.generateClassFile(); // 是否要將生成代理類的位元組碼檔案儲存到磁碟中 if (saveGeneratedFiles) { // .... } return var4; }
在測試案例中,設定系統屬性sun.misc.ProxyGenerator.saveGeneratedFiles值為true
// 儲存生成的代理類的位元組碼檔案 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
開啟$Proxy0.class檔案如下:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.sun.proxy; import com.lnjecit.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 m3; private static Method m2; private static Method m0; public $Proxy0(InvocationHandler var1) throws { super(var1); } public final boolean equals(Object var1) throws { try { return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue(); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final void doSomething() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } 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 int hashCode() throws { try { return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue(); } 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.lnjecit.proxy.Subject").getMethod("doSomething"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
可看到
1、代理類繼承了Proxy類並且實現了要代理的介面,由於java不支援多繼承,所以JDK動態代理不能代理類
2、重寫了equals、hashCode、toString
3、有一個靜態程式碼塊,通過反射或者代理類的所有方法
4、通過invoke執行代理類中的目標方法doSomething
參考資料: