天天看點

JDK動态代理原理JDK動态代理原理

JDK動态代理原理

通過一個例子引入JDK動态代理

需求描述

Dao

層類中的方法進行增強。例如:使用日志記錄操作資料庫所花的時間

類結構圖

JDK動态代理原理JDK動态代理原理

Mapper接口定義

public interface Mapper {
    void insert();
}
           

Mapper接口實作類

public class RealMapper implements Mapper {
    @Override
    public void insert() {
        System.out.println("do insert...");
    }
}
           

代理處理邏輯的封裝

public class MapperInvocationHandler implements InvocationHandler {
    Object target;

    public MapperInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("do something...");
        return method.invoke(target, args);
    }
}
           

代理對象工廠

public class MapperProxyFactory {
    public static Object newInstance(Object realMapper, Class<?>... interfaces) {
        return Proxy.newProxyInstance(MapperProxyFactory.class.getClassLoader(), interfaces, new MapperInvocationHandler(realMapper));
    }
}
           

用戶端

public class Client {
    public static void main(String[] args) {
        RealMapper real = new RealMapper();
        Mapper mapper = (Mapper) MapperProxyFactory.newInstance(real, Mapper.class);
        mapper.insert();
    }
}
           

結果驗證

JDK動态代理原理JDK動态代理原理

可以看到最終我們實作了對原有實作的增強,下面我們源碼檢視内部的實作原理。

實作原理

Proxy#newProxyInstance

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
    {
        Objects.requireNonNull(h);
        final Class<?>[] intfs = interfaces.clone();
        // 删除一些安全的驗證代碼
        Class<?> cl = getProxyClass0(loader, intfs);
        try {
        	// 通過反射建立代理對象
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            return cons.newInstance(new Object[]{h});
        } catch (Exception e) {
			// 異常的處理
        }
    }
           

該方法執行了以下幾步

  1. 參數檢查以及資料準備
  2. 調用

    getProxyClass0

    方法擷取Class對象
  3. 通過反射建立代理對象并傳回

擷取Class對象(Proxy#getProxyClass0)

private static Class<?> getProxyClass0(ClassLoader loader,
                                       Class<?>... interfaces) {
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }

    // If the proxy class defined by the given loader implementing
    // the given interfaces exists, this will simply return the cached copy;
    // otherwise, it will create the proxy class via the ProxyClassFactory
    return proxyClassCache.get(loader, interfaces);
}
           

ProxyClassFactory#apply

public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
		// nextUniqueNumber是目前類的靜态常量
		// 是原子類 AtomicLong nextUniqueNumber = new AtomicLong();
        long num = nextUniqueNumber.getAndIncrement();
        // proxyPkg是包路徑、eg:com.keminapera.proxy
        // proxyClassNamePrefix是目前類的靜态常量
        // 始終固定不變 String proxyClassNamePrefix = "$Proxy";
		// 是以使用JDK動态代理生成的類名格式:$Proxy+一個數字  eg:$Proxy0、$Proxy1
        String proxyName = proxyPkg + proxyClassNamePrefix + num;
		// 這裡通過ProxyGenerator生成代理類的位元組數組
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
        try {
        	// 将上面生成代理類的位元組數組通過指定的loader加載到JVM并得到該代理類的Class對象
        	// 對應的類名就是上面動态生成的proxyName---com.keminapera.proxy.$Proxy0
            return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
        } catch (ClassFormatError e) {
            // 處理異常
        }
    }
}
           

代理類建立時序圖

JDK動态代理原理JDK動态代理原理

ProxyGenerator#generateProxyClass測試

上面我們知道最終通過調用ProxyGenerator類的generateProxyClass靜态方法生成代理類的位元組數組,我們通過模拟ProxyClassFactory#apply調用該方法來檢視生成代理類的格式是什麼樣的。

測試類

public class ProxyGeneratorTest {
    private static final String proxyClassNamePrefix = "$Proxy";
    private static final AtomicLong nextUniqueNumber = new AtomicLong();
    public static void main(String[] args) throws Exception {
        String proxyClassName = getProxyClassName();
        byte[] bytes = ProxyGenerator.generateProxyClass(proxyClassName, new Class[]{Mapper.class}, Modifier.PUBLIC);
        genetateClassFile(bytes, "E:\\Chrome_Workspace", proxyClassName);
    }
	// 動态生成類名
    private static String getProxyClassName() {
        String name = ProxyGeneratorTest.class.getName();
        name = name.substring(0, name.lastIndexOf('.') + 1);
        return name + proxyClassNamePrefix + nextUniqueNumber.get();
    }

	// 讀取class檔案轉換成byte[]
    static void genetateClassFile(byte[] bytes, String filePath, String fileName) throws Exception {
        fileName = fileName.replace(".","//");
        // 此處是上面定義好的Mapper.java編譯後的class檔案路徑
        File file = new File("E:\\Chrome_Workspace\\Mapper.class");
        if (!file.exists()){
            file.createNewFile();
        }
        try(FileOutputStream fos = new FileOutputStream(file)){
            fos.write(bytes);
            fos.flush();
        }
    }
}
           

生成的内容

package com.keminapera.jdkapi.reflection.proxy;

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

public class $Proxy0 extends Proxy implements Mapper {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    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});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void insert() 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.keminapera.jdkapi.reflection.proxy.Mapper").getMethod("insert");
            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類
  • 重寫了

    Object

    類的

    equals

    hashCode

    toString

    方法
  • 實作了接口的所有方法,内部都是調用

    InvocationHandler

    實作類的

    invoke

    方法

結論

ProxyGenerator#generateProxyClass方法傳回的是編譯生成的位元組碼的位元組數組

JDK動态代理原理JDK動态代理原理

代理類執行個體化階段

通過上面的流程我們已經拿到了代理對象的Class對象,有了Class對象通過反射可以生成一個真實對象。我們再次回到Proxy#newProxyInstance方法檢視代理對象執行個體化部分代碼。

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

    final Class<?>[] intfs = interfaces.clone();
    Class<?> cl = getProxyClass0(loader, intfs);
    // 代理對象執行個體化階段開始
    try {
        // 通過反射擷取定值參數的代理對象構造器
        // constructorParams參數是Proxy類的靜态常量 
        // Class<?>[] constructorParams = { InvocationHandler.class };
        final Constructor<?> cons = cl.getConstructor(constructorParams); 
        final InvocationHandler ih = h;  
        // 通過構造器反射執行個體化代理對象
        return cons.newInstance(new Object[]{h}); 
    } catch (Exception e) {
        //異常的處理
    }
           

通過反射擷取指定參數的構造器,每個動态生成的代理類都會有該構造器,擷取如下構造方法

public $Proxy0(InvocationHandler var1) throws  {
	super(var1);
}
           

該構造器調用父類構造器,從上面

ProxyGenerator

的傳回結果可以知道生成的代理類繼承

Proxy

類,是以調用

Proxy

的構造方法

protected Proxy(InvocationHandler h) {
    Objects.requireNonNull(h);
    this.h = h;
}
           

最終設定

Proxy

的成員變量

h

代理對象生成時序圖

代理類生成的方法(Mapper接口的insert方法)

public final void insert() throws  {
    try {
        super.h.invoke(this, m3, (Object[])null);
    } catch (RuntimeException | Error var2) {
        throw var2;
    } catch (Throwable var3) {
        throw new UndeclaredThrowableException(var3);
    }
}
           

通過檢視代理類中生成的方法會發現所有方法最終都調用的成員變量

h

invoke

方法,是以可以看出最後代理邏輯的處理是在使用者定義的InvocationHandler實作類的invoke方法中實作的。整體的調用關系圖如下:

JDK動态代理原理JDK動态代理原理

動态代理的使用場景

  • Mybatis架構中使用Mapper接口操作資料庫Mybatis源碼解析–Mapper代理對象
  • rpc架構實作遠端方法調用
  • Spring中對實作接口的類增強

小結

這塊不得不提一下靜态代理,從本質上來說動态代理就是将靜态代理中由開發人員建立的代理類現在變成由JVM建立,如下圖所示:

JDK動态代理原理JDK動态代理原理

但靜态代理有一個緻命的缺點就是如果有成百上千個類需要增強,那就要為每一個類建立對應的增強邏輯,而且增強邏輯一模一樣;這樣會導緻類爆炸和代碼備援

JDK動态代理原理JDK動态代理原理

由于對這些類的增強邏輯是相同的,完全可以将其抽到一個類中實作,如果後續增強邏輯變了,隻需要修改這一處即可,友善後續維護。由于現在代理類交給JVM去生成,是以它必須知道調用代理對象的方法時該交給誰去處理,是以JDK定義了一個接口

InvocationHandler

讓開發人員去實作。

JDK動态代理原理JDK動态代理原理

通過上面的分析靜态代理和動态代理的差別就在于代理類交給JVM在運作期建立,進而減少代理類的數量。實作這一目标的手段是通過約定了一個

InvocationHandler

接口,JVM生成的代理類的方法全部交給

InvocationHandler#invoke

方法去處理,而該方法需要開發人員去實作。是以一般我們在

InvocationHandler

的實作類中會持有一個真正幹活的目标對象,對方法攔截增強之後還是要調用目标對象的原有邏輯。

JDK動态代理的缺點:被代理類必須實作接口