【java_設計模式】jdk動態代理原碼解析,debug 獲取 $proxy0

普通測試場景

  • 業務場景
    保存訂單信息
  • 分層模型
    IOrderService -> OrderServiceImpl
  • 業務代碼
public class OrderServiceImpl implements IOrderService {
	// spring 會自己注入,這裏手動注入
    private IOrderDao iOrderDao;

    @Override
    public int saveOrder(Order order) {
        iOrderDao = new OrderDaoImpl();
        // 暫時不連接數據庫
        return 1;
    }
}
  • 應用層代碼
public class Test {
    public static void main(String[] args) throws Throwable {
        IOrderService orderServiceDynamicProxy
                = (IOrderService) new OrderServiceDynamicProxy( new OrderServiceImpl()).bind();
        // 數據準備
        Order order = new Order();
        // 使用動態代理存數據
        orderServiceDynamicProxy.saveOrder(order);
    }
}
  • 自定義的代理代碼

實現 InvocationHandler 即可成爲動態代理類, 爲什麼? 需要分析源碼

public class OrderServiceDynamicProxy implements InvocationHandler {

    // 被代理的對象, 抽象成Object類, 甚至可以不是接口,提高了最大的靈活性
    private Object target;
    
    /**
     * 核心方法 -- 動態生成被代理類
     * @return JVM生成的 $proxy0 文件
     */
    public Object bind(){
        // 獲取代理類的class文件
        Class cls = target.getClass();
        ClassLoader classLoader = cls.getClassLoader();
        Class[] interfaces = cls.getInterfaces();
        return Proxy.newProxyInstance(classLoader, interfaces,this);
    }
    
    public OrderServiceDynamicProxy(Object target) {
        this.target = target;
    }
    
    /**
     * 核心方法 -- 需要debug確定該方法的作用
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object argObject = args[0];
        beforeMethod(argObject);
        Object object = method.invoke(target, args);
        afterMethod();
        return object;
    }
    
    private void beforeMethod(Object obj){}

    private void afterMethod(){}
}

1 jdk源碼分析 – debug

  1. 返回值起源 – 確定返回值
    return Proxy.newProxyInstance(classLoader, interfaces,this)

    Proxy.newProxyInstance 返回的是 $Proxy0對象
    在這裏插入圖片描述

  2. 返回值追蹤 – 定位類
    Proxy類擁有創造前綴爲$Proxy 的工廠內部類(jdk1.8)

    確定返回值來源類: java.lang.reflect.Proxy.ProxyClassFactory

    在這裏插入圖片描述

  3. 返回值追蹤 – 定位語句

    639行出現了一個字節數組,爲proxyClassFile,幾乎能斷定這是動態生成的 $proxy0 字節碼文件
    在這裏插入圖片描述


  1. 使用idea工具提取該文件

    在這裏插入圖片描述

    FileOutputStream fos = new FileOutputStream("D:\\b.class");
    fos.write(proxyClassFile);
    

2 生成的 $proxy0 源碼

public final class $Proxy0 extends Proxy implements IOrderService {

    private static Method m3;

    /**
     * 構造方法傳入InvocationHandler的實現類
     * todo 傳入InvocationHandler 的實現類 OrderServiceDynamicProxy
     *
     * @param var1 OrderServiceDynamicProxy
     */
    public $Proxy0(InvocationHandler var1) {
        super(var1);
    }

    /**
     *
     * 程序調用的是這個方法,而真正執行的是 OrderServiceDynamicProxy 的 saveOrder 方法
     * todo 動態生成的 saveOrder() 方法實際調用的是 OrderServiceDynamicProxy 的invoke方法
     * todo invoke 方法是約定而成的模板方法
     *
     * @param var1 在靜態代碼塊中獲得的參數列表
     *
     */
    public final int saveOrder(Order methodVar) throws Throwable { // 參數列表的數量和類型也是從class文件中獲取的
        // protected InvocationHandler h;
        // todo h 是父類Proxy類的成員變量,也就是構造時傳入的 OrderServiceDynamicProxy
        return (Integer) super.h.invoke(this, m3, new Object[]{methodVar});
    }
    /**
     *  todo 靜態代碼塊先執行
     */
    static {
        try {
            m3 = Class.forName("com.structural.proxy.IOrderService").getMethod("saveOrder", Class.forName("com.structural.proxy.Order"));
        }catch (Exception var2) {
            var2.printStackTrace();
        }
    }

}

3 $proxy0 源碼分析

靜態代碼塊通過反射獲取調用者的class文件信息

    /**
     *  todo 靜態代碼塊先執行
     */
    static {
        try {
            m3 = Class.forName("com.structural.proxy.IOrderService").getMethod("saveOrder", Class.forName("com.structural.proxy.Order"));
        }catch (Exception var2) {
            var2.printStackTrace();
        }
    }
突破口1 —— $proxy0 的成員方法是由應用層決定

自定義的bind方法中存在以下邏輯,可以確定 $proxy0 內的方法不需要事先知道,
在應用層調用的時候傳入對象,使用對象的class文件可以反射獲取

// 調用bind方法的應用層
        IOrderService orderServiceDynamicProxy
                = (IOrderService) new OrderServiceDynamicProxy( new OrderServiceImpl()).bind();
// 代理類構造方法
	public OrderServiceDynamicProxy(Object target) {
        this.target = target;
    }
// bind方法邏輯
        Class cls = target.getClass();
        ClassLoader classLoader = cls.getClassLoader();
        Class[] interfaces = cls.getInterfaces();  // 決定了 $Proxy0 implement IOrderService
        return Proxy.newProxyInstance(classLoader, interfaces,this);

突破口2 —— InvocationHandler

Proxy.newProxyInstance(,,this)
this 指的是 OrderServiceDynamicProxy implments InvocationHandler

	public $Proxy0(InvocationHandler var1) {
	    super(var1);
	}
    public final int saveOrder(Order methodVar) throws Throwable {
        // protected InvocationHandler h;
        // todo h 是父類Proxy類的成員變量,也就是構造時傳入的 OrderServiceDynamicProxy
        return (Integer) super.h.invoke(this, m3, new Object[]{methodVar});
    }

super.h.invoke(this, m3, new Object[]{methodVar} )
等價於
OrderServiceDynamicProxy.invoke(this, m3, new Object[]{methodVar})
則應用層

public class Test {
    public static void main(String[] args) throws Throwable {
        IOrderService orderServiceDynamicProxy
                = (IOrderService) new OrderServiceDynamicProxy( new OrderServiceImpl()).bind();
        // 數據準備
        Order order = new Order();
        // 使用動態代理存數據
        orderServiceDynamicProxy.saveOrder(order);
    }
}

orderServiceDynamicProxy.saveOrder(order)
內部調用的是
OrderServiceDynamicProxy.invoke(this, m3, new Object[]{order})

突破口3 —— OrderServiceDynamicProxy.invoke 與 動態代理的關係

動態代理類只要實現Invoke方法,並且確保method.invoke(target, args)即可代理target的所有方法, 還可以加上自己的前置和後置處理,強制代理類實現的原因是:
$Proxy0 的生成模板有super.h.invoke(this, m3, new Object[]{…args})
所以應用層需要適配這個實現

 /**
     * 核心方法 -- 需要debug確定該方法的作用
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object argObject = args[0];
        beforeMethod(argObject);
        Object object = method.invoke(target, args);
        afterMethod();
        return object;
    }

總結

動態代理的實現核心是 藉助 $Proxy0 在運行時生成一個被代理類的class文件,代理類不需要在編譯時寫死被代理類
實現 InvocationHandler 是讓動態代理強制適配 $Proxy0 的生成規則
能夠做到在應用層動態決定被代理類,起到決定性的技術是反射

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章