高級JAVA - 動態代理的實現原理和源碼分析

在之前的一篇文章中 , 我們簡單瞭解了一下代理模式(JAVA設計模式 - 代理模式) , 本篇我們來學習一下動態代理的實現原理 , 以及源碼是怎樣的 . 

JDK動態代理的主要實現步驟如下 : 

1 . 聲明一個handler類 , 實現InvovationHandler接口 , 然後重寫它的invoke方法 , 在此方法中完成擴展邏輯 . 在生成動態代理類之後 , 原方法的調用就是會執行這個invoke方法

2 . 利用Proxy類的靜態方法newProxyInstance創建了一個動態代理實例 , 傳入了類加載器,需要實現的接口,方法調用的實際處理者,即第一步的handler類 , 然後查看該方法的源碼 , 發現它爲我們封裝了創建動態代理類的步驟 , 返回了一個代理對象實例

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
        Objects.requireNonNull(h);//handler不能爲null

        final Class<?>[] intfs = interfaces.clone();//代理類需要實現的接口
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        Class<?> cl = getProxyClass0(loader, intfs);//通過loader和接口 , 得到代理的class對象 , 同時會把class對象緩存一份 , 在代理對象存活於內存中的這段時間 , 就不再創建新的class實例
        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});//創建代理對象的實例 , 並將InvocationHandler傳入
        } 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);
        }
    }

經過以上操作 , 就在內存中新建了一個擁有我們擴展行爲的代理類

3 . 在動態代理中 , 代理類是在內存中動態生成的 , 所以我們沒法查看裏面有什麼內容 , 所以我們要想辦法先把動態代理生成的內容顯示出來 , 先看下內部結構如何 . 該操作通過如下工具類完成

package com.xbz.xstudy.shejimoshi.proxy;

import org.springframework.cglib.core.DefaultGeneratorStrategy;
import org.springframework.cglib.proxy.Enhancer;
import sun.misc.ProxyGenerator;

import java.nio.file.Files;
import java.nio.file.Paths;

/**
 * @title 用來將代理對象輸出到文件 , 便於分析
 * @author Xingbz
 * @createDate 2019-4-23
 */
public interface ProxyClassOutUtil {

    static void jdkOut(String className, Class[] classArr, String outPath) {
        try {
            Files.write(Paths.get(outPath), ProxyGenerator.generateProxyClass(className, classArr));
        } catch (Exception e) {
            System.err.println("JDK代理對象輸出異常 . " + e.getMessage());
        }
        System.out.println("JDK代理對象輸出完成");
    }

    static void cdlibOut(Enhancer enhancer, String outPath) {
        try {
            Files.write(Paths.get(outPath), DefaultGeneratorStrategy.INSTANCE.generate(enhancer));
        } catch (Exception e) {
            System.err.println("CGLIB代理對象輸出異常 . " + e.getMessage());
        }
        System.out.println("CGLIB代理對象輸出完成");
    }
}

我們在JDK動態代理測試類方法末尾增加一行

package com.xbz.xstudy.shejimoshi.proxy.dynamic.jdk;

import com.xbz.xstudy.shejimoshi.proxy.ProxyClassOutUtil;
import com.xbz.xstudy.shejimoshi.proxy.targetObject.ActorInterface;
import com.xbz.xstudy.shejimoshi.proxy.targetObject.ActorInterfaceImpl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

/**
 * @title JDK動態代理測試類
 * @author Xingbz
 * @createDate 2019-4-23
 */
public class Demo {
    public static void main(String[] args){
        ActorInterface actor = new ActorInterfaceImpl();

        InvocationHandler proxyFactory = new ProxyHandler(actor);

        ActorInterface actorProxy = (ActorInterface) Proxy.newProxyInstance(ActorInterface.class.getClassLoader(), new Class[]{ActorInterface.class}, proxyFactory);

        actorProxy.show();

        //將動態生成的代理對象輸出到文件
        ProxyClassOutUtil.jdkOut("ActorInterface$Proxy0", ActorInterfaceImpl.class.getInterfaces(),"target\\ActorInterface$Proxy0.class");
    }
}

然後我們就可以在target\下發現一個ActorIntreface$Porxy.class , 打開內容如下 : 

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import com.xbz.xstudy.shejimoshi.proxy.targetObject.ActorInterface;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class ActorInterface$Proxy0 extends Proxy implements ActorInterface {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public ActorInterface$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 show() 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);
        } 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.xbz.xstudy.shejimoshi.proxy.targetObject.ActorInterface").getMethod("show");
            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());
        }
    }
}

可以看到該類繼承了Proxy父類且實現了與目標對象相同的接口 , 接下來重點關注下show方法 , 裏面有這麼一行

super.h.invoke(this, m3, (Object[])null);

所以該方法的最終實現 , 就是調用了super.h(即父類Proxy的h)來處理 , 那麼h又是從何而來呢? 再來看一下Proxy中的源碼

public class Proxy implements java.io.Serializable {
	//...
    protected InvocationHandler h;
	//...
}

沒錯 , 就是我們聲明的一個類來最終完成的這些操作 . 所以JDK動態代理大致的步驟如下

  1. 聲明一個InvocationHandler , 來編寫我們的業務擴展代碼 , 並調用method.invoke(targetObject,args);來執行原來的目標對象方法
  2. 調用Proxy的靜態方法newProxyInstance() , 並傳入我們自定義的InvocationHandler . 然後Proxy爲我們返回一個擁有handler的代理對象
  3. 執行代理對象的方法 , 代理對象通過調用handler的invoke , 就實現了動態代理
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章