jdk 動態代理的主要三個部分
1. Proxy 類.
2. ClassLoader
3.InvocationHandler
java中動態代理主要有JDK和CGLIB兩種方式。
區別主要是jdk是代理接口,而cglib是代理類。
jdk的動態代理調用了Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法。
通過該方法生成字節碼,動態的創建了一個代理類,interfaces參數是該動態類所繼承的所有接口,而繼承InvocationHandler 接口的類則是實現在調用代理接口方法前后的具體邏輯,下邊是具體的實現:
public class Test { static interface Subject{ void sayHi(); void sayHello(); } static class SubjectImpl implements Subject{ @Override public void sayHi() { System.out.println("hi"); } @Override public void sayHello() { System.out.println("hello"); } } static class ProxyInvocationHandler implements InvocationHandler{ private Subject target; public ProxyInvocationHandler(Subject target) { this.target=target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.print("say:"); return method.invoke(target, args); } } public static void main(String[] args) { Subject subject=new SubjectImpl(); Subject subjectProxy=(Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), new ProxyInvocationHandler(subject)); subjectProxy.sayHi(); subjectProxy.sayHello(); } }
/** * * JDK動態代理類 * * */ public class JDKProxy implements InvocationHandler { private Object targetObject;//需要代理的目標對象 public Object newProxy(Object targetObject) {//將目標對象傳入進行代理 this.targetObject = targetObject; return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);//返回代理對象 } public Object invoke(Object proxy, Method method, Object[] args)//invoke方法 throws Throwable { before(); Object ret = null; // 設置方法的返回值 ret = method.invoke(targetObject, args); //invoke調用需要代理的方法 after(); return ret; } private void before() {//方法執行前 System.out.println("方法執行前 !"); } private void after() {//方法執行后 System.out.println("方法執行后"); } }
newProxyInstance方法執行了以下幾種操作。
1.生成一個實現了參數interfaces里所有接口且繼承了Proxy的代理類的字節碼,然后用參數里的classLoader加載這個代理類。
2.使用代理類父類的構造函數 Proxy(InvocationHandler h)來創造一個代理類的實例,將我們自定義的InvocationHandler的子類傳入。
3.返回這個代理類實例。
在main方法中加入System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"),這樣就會把生成的代理類Class文件保存在本地磁盤上,然后再反編譯可以得到代理類的源碼:
public final class $Proxy0 extends Proxy implements Test.Subject { private static Method m4; private static Method m1; private static Method m3; private static Method m0; private static Method m2; static { try { m4 = Class.forName("Test$Subject").getMethod("sayHello", new Class[0]); m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m3 = Class.forName("Test$Subject").getMethod("sayHi", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); } catch (Exception e) { throw new RuntimeException(e); } } public $Proxy0(InvocationHandler paramInvocationHandler) { super(paramInvocationHandler); } public final void sayHello() { try { this.h.invoke(this, m4, null); return; } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final boolean equals(Object paramObject) { try { return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final void sayHi() { try { this.h.invoke(this, m3, null); return; } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final int hashCode() { try { return ((Integer)this.h.invoke(this, m0, null)).intValue(); } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final String toString() { try { return (String)this.h.invoke(this, m2, null); } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } }
我們可以看到代理類內部實現比較簡單,在調用每個代理類每個方法的時候,都用反射去調newProxyInstanceh方法中傳來的h的invoke方法(也就是我們自定義的InvocationHandler的子類中重寫的invoke方法),用參數傳遞了代理類實例、接口方法、調用參數列表,這樣我們在重寫的invoke方法中就可以實現對所有方法的統一包裝了。