>>>>>>>>>>>>>>>>>>>>>>>>>>>>>本文僅限於jdk代理,或者說是基於接口的動態代理>>>>>>>>>>>>>>>>>>>>>>>>>>>>>java
Java的動態代理與兩個重要的類,或者說方法有關。面試
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);sql
InvocationHandler.invoke(Object proxy, Method method, Object[] args);緩存
舉個例子,咱們有一個接口Singer,有幾個方法,如songList(),name(),style()等。mybatis
public interface Singer { void songList(); void name(); void style(); }
這個會有不少的實現類,如JayChou啊,JackyCheung啊等等。架構
像這種,存在實現類的接口,動態代理比較簡單。就是建立一個InvocationHandler的實現類,讓他存在一個Object屬性,而且存在一個構造函數,設定這個object爲具體的實現類便可。app
public class SingerProxy implements InvocationHandler { private Object target; public SingerProxy(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before... something todo..."); Object invoke = method.invoke(target, args); System.out.println("After... something todo..."); return invoke; } }
測試方法:ide
public class Main { public static void main(String[] args) { Singer singer = new JackeyCheung(); Singer proxy = (Singer) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), singer.getClass().getInterfaces(), new SingerProxy(singer)); proxy.name(); proxy.style(); proxy.songList(); } }
InvocationHandler是一個接口,他的實現類(SingerProxy)是具體的對象的代理實現者。函數
他只有一個方法測試
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
proxy:在其上調用方法的代理實例
method:被代理的方法
args:被代理的方法的參數們
第二和第三個參數比較容易理解,通常調用method.invoke(target, args)就能夠了。可是這個proxy參數比較費解。
實際上,他是一個相似於$Proxy0這種類的一個實例。
若是在Main中的main方法中追加一段以下的代碼
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
會在項目目錄下生成一個字節碼文件以下所示:
public final class $Proxy0 extends Proxy implements Singer { private static Method m1; private static Method m5; private static Method m2; private static Method m3; private static Method m0; private static Method m4; 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}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final void name() throws { try { super.h.invoke(this, m5, (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 void style() 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); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final void songList() throws { try { super.h.invoke(this, m4, (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")); m5 = Class.forName("dynamic.singer.Singer").getMethod("name"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m3 = Class.forName("dynamic.singer.Singer").getMethod("style"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); m4 = Class.forName("dynamic.singer.Singer").getMethod("songList"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
這個$Proxy0就是這個類的名稱,這裏也能看出jdk動態代理只能代理接口,由於這個類已經有一個Proxy父類,而且java不支持多繼承。
這個類中的super.h就是咱們本身定義個InvocationHandler的實現類。也就是去調用咱們實現的invoke方法。
這裏注意幾個點,$Proxy0這個類把Object的toString, equals, hashcode三個方法也給代理了,toString這個方法咱們最好讓InvocationHandler的實現類單獨實現一下,否則可能會出現堆棧溢出。
咱們知道invoke方法的第一個參數是$Proxy0的一個實例了。有的時候,咱們會在invoke中使用this,這個this指代的確定是InvocationHandler的實現對象了。
那麼proxy參數到底有什麼用呢?
當方法沒有返回值的時候,確實沒有什麼用途,invoke方法返回null均可以。若是存在返回值,且進行連續調用時。一個方法的調用返回自身,StringBuilder的append這種,可是StringBuilder不是一個接口因此不適用,咱們能夠本身製造一個SBuilder。
public interface SBuilder { SBuilder append(String s); }
這種時候,能夠將proxy直接返回,方便下一次調用。實際上,不連續調用就能夠了。
還有另一種狀況,就是隻存在接口,沒有實現類時,也能夠實現動態代理。比較知名的應該就是mybatis了,只存在一個Mapper接口,還有一個對應的mapper.xml文件。
他的內部原理也是動態代理。
他是可以經過分析mapper文件,獲取到mapper的方法,參數,返回值的,若是和Mapper接口中的匹配的話,就能夠去調用method.invoke();
具體能夠查看mybatis的MapperProxy類的invoke方法。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if (Object.class.equals(method.getDeclaringClass())) {// Object的方法的話,toString, equals, hashcode直接調用mapperProxy的 return method.invoke(this, args); } if (this.isDefaultMethod(method)) {// 若是存在具體的實現,走default方法 return this.invokeDefaultMethod(proxy, method, args); } } catch (Throwable var5) { throw ExceptionUtil.unwrapThrowable(var5); } MapperMethod mapperMethod = this.cachedMapperMethod(method); return mapperMethod.execute(this.sqlSession, args); }
最後這塊就是將每個method都緩存一下,供後期調用。
mapperMethod的execute就是具體的實現了。內容不重要,不是動態代理的範疇,實現方式很巧妙。
public Object execute(SqlSession sqlSession, Object[] args) { Object result; switch (command.getType()) { case INSERT: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.insert(command.getName(), param)); break; } case UPDATE: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.update(command.getName(), param)); break; } case DELETE: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param)); break; } case SELECT: if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { result = executeForMap(sqlSession, args); } else if (method.returnsCursor()) { result = executeForCursor(sqlSession, args); } else { Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); } break; case FLUSH: result = sqlSession.flushStatements(); break; default: throw new BindingException("Unknown execution method for: " + command.getName()); } if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) { throw new BindingException("Mapper method '" + command.getName() + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ")."); } return result; }
動態代理是一個很重要的比較複雜的java內容,架構設計跟面試時,使用頻率比較高。