JDK動態代理java
jdk自帶的動態代理主要是經過實現InvocationHandler數組
InvocationHandler的主要方法緩存
Object invoke(Object proxy, Method method,Object[] args)throws Throwable安全
在代理實例上處理方法調用並返回結果。在與方法關聯的代理實例上調用方法時,將在調用處理程序上調用此方法。即調用真實業務的方法都會進入到此invoke方法,至於爲何,稍後再說明多線程
方法詳細介紹app
Method-指代具體被代理的方法。
args -包含傳入代理實例上方法調用的參數,若是接口方法不使用參數,則爲 null。ide
return: 從代理實例的方法調用返回的值。函數
throws: Throwable - 從代理實例上的方法調用拋出的異常。測試
少說點廢話吧 ,關於什麼是動態代理網上不少介紹,這裏直接從源碼開始。。ui
public interface HelloWorld { void say(); }
public class HelloWorldImp implements HelloWorld { @Override public void say() { System.out.println("hello world!"); } }
public class HelloProxy implements InvocationHandler { private Object target;//被代理對象 public Object bind(Object target){ this.target = target; return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("berfor....."); method.invoke(target, args); System.out.println("after....."); return null; } }
public class Client { public static void main(String[] args) { HelloProxy h = new HelloProxy(); HelloWorld helloWorld = (HelloWorld) h.bind(new HelloWorldImp()); helloWorld.say(); } }
咱們利用ProxyGenerator類的generateProxyClass方法生成代理對象的class文件在idea中打開,豁然開朗其實主要一步就在於生成類的過程,類文件中創建被代理對象接口的invoke調用
String path = "D://aaa.class"; byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", HelloWorldImp.class.getInterfaces()); FileOutputStream out = null; try { out = new FileOutputStream(path); out.write(classFile); out.flush(); } catch (Exception e) { e.printStackTrace(); } finally { try { out.close(); } catch (IOException e) { e.printStackTrace(); } }
下面直接看源碼吧
public final class $Proxy0 extends Proxy implements HelloWorld { private static Method m1; private static Method m2; private static Method m3; 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 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 void say() throws { try { super.h.invoke(this, m3, (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")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m3 = Class.forName("com.canyou.proxy.HelloWorld").getMethod("say"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
生成的代理對象一個有4個方法,其中就有咱們的say方法,裏面調用handle的invoke方法,其餘三個是全部類都有的,直接用的Method的反射機制實現的代理。
3、代理類的生成
那jdk源碼是怎麼生成代理類的呢?
首先看Proxy類的newProxyInstance方法吧
/** * 這裏有三個參數, * 第一個是傳入classloader,通常狀況是傳入當前的classloader. * 第二個參數表示的是接口, * 第三個是Invocationhandler */ @CallerSensitive 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<?> cl = getProxyClass0(loader, intfs); //生成構造函數 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); } }
而後看getProxyClass0方法
private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } //從緩存中獲取,若是不存在就建立 return proxyClassCache.get(loader, interfaces); }
使用proxyClassCache作緩存,其目的是爲了複用,同時防止多線程重複建立
//獲取或生成代理類 此處由於不是線程安全的作了屢次判斷 public V get(K key, P parameter) { Objects.requireNonNull(parameter); //刪除過時條目 expungeStaleEntries(); //建立cacheKey Object cacheKey = CacheKey.valueOf(key, refQueue); //查看key是否已經存在valuemaps中 ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey); if (valuesMap == null) { //不存在的話經過,再次嘗試嘗試獲取,若是沒有就插入 ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey, valuesMap = new ConcurrentHashMap<>()); if (oldValuesMap != null) { valuesMap = oldValuesMap; } } //生成代理對象的key 爲弱引用類型,這裏重要的subKeyFactory.apply方法 Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); //嘗試從valuemap中獲取 Supplier<V> supplier = valuesMap.get(subKey); Factory factory = null; //這個後面的邏輯基本上是若是建立已經有了 就替換或者直接添加到緩存 while (true) { if (supplier != null) { V value = supplier.get(); if (value != null) { return value; } } // else no supplier in cache // or a supplier that returned null (could be a cleared CacheValue // or a Factory that wasn't successful in installing the CacheValue) // lazily construct a Factory if (factory == null) { factory = new Factory(key, parameter, subKey, valuesMap); } if (supplier == null) { supplier = valuesMap.putIfAbsent(subKey, factory); if (supplier == null) { // successfully installed Factory supplier = factory; } // else retry with winning supplier } else { if (valuesMap.replace(subKey, supplier, factory)) { // successfully replaced // cleared CacheEntry / unsuccessful Factory // with our Factory supplier = factory; } else { // retry with current supplier supplier = valuesMap.get(subKey); } } } }
ProxyClassFactory.apply中的重要片斷方法,全部代理的class對象就這麼來的
/* * 這裏纔是重點 就像以前生成的class文件同樣 * 生成代理類的字節數組 */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { /* * A ClassFormatError here means that (barring bugs in the * proxy class generation code) there was some other * invalid aspect of the arguments supplied to the proxy * class creation (such as virtual machine limitations * exceeded). */ throw new IllegalArgumentException(e.toString()); }
//生成代理類 public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) { ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2); /** * 生成具體文件字節數組 * 1.找到全部接口的方法 * 2.添加object類的三個方法 tostring hashcode equils * 3.遍歷生成具體的代理方法,代理方法的邏輯都想似,回調咱們的代理類 */ final byte[] var4 = var3.generateClassFile(); // private static final boolean saveGeneratedFiles = GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"))).booleanValue(); //設置sun.misc.ProxyGenerator.saveGeneratedFiles = true,就會生成代理類class的文件 if (saveGeneratedFiles) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { try { int var1 = var0.lastIndexOf(46); Path var2; if (var1 > 0) { //生成path 將.替換成系統文件分隔符 Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar)); //建立文件夾 Files.createDirectories(var3); //具體文件 var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class"); } else { //沒包就放在項目根目錄下 var2 = Paths.get(var0 + ".class"); } //寫入到文件中 Files.write(var2, var4, new OpenOption[0]); return null; } catch (IOException var4x) { throw new InternalError("I/O exception saving generated file: " + var4x); } } }); } return var4; }
之前老是知道有動態代理這麼一回事,理解也不是很深,經過源代碼的閱讀,對JDK的動態代理實現清晰不少吧,應該對後面的Spring AOP的具體實現會更有幫助。不過裏面也有不少地方沒有深刻了,仍是能力不夠吧,好比緩存代理,權限驗證等。