Java 代理使用與原理

代理 指的是代表授權(quán)方執(zhí)行處理事務(wù)。在編程中,通常是通過一個(gè)代理對象代表目標(biāo)對象去執(zhí)行方法,是對調(diào)用目標(biāo)的一個(gè)包裝。這樣來保證目標(biāo)對象方法的安全性、或者增強(qiáng)目標(biāo)對象的方法功能。

Java 有 3 種代理方式:

靜態(tài)代理

通過手動創(chuàng)建代理對象,來實(shí)現(xiàn)對目標(biāo)對象的代理。在這過程中,一般存在 3 種對象:客戶端、目標(biāo)對象、代理對象。
客戶端通過代理對象去調(diào)用目標(biāo)對象的真實(shí)方法。
對象的接口

public interface IService {
    void request();
}

真正的目標(biāo)對象

public class RealService implements IService {
    @Override
    public void request() {
        System.out.println("真正處理請求內(nèi)容");
    }
}

代理類

public class ProxyService implements IService {
    private IService realService;

    public ProxyService(IService service) {
        this.realService = service;
    }

    @Override
    public void request() {
        // ...執(zhí)行真實(shí) request() 前其他邏輯
        System.out.println("request() 方法前邏輯");
        this.realService.request();
        System.out.println("request() 方法后邏輯");
        // ...執(zhí)行真實(shí) request() 后其他邏輯
    }
}

客戶端實(shí)現(xiàn)

public class StaticProxyClient {
    public void callRequest() {
        // 目標(biāo)對象
        IService realService = new RealService();
        // 代理對象
        IService proxyService = new ProxyService(realService);
        // 通過代理對象執(zhí)行真正的邏輯,并且在代理對象中對請求進(jìn)行增強(qiáng)
        proxyService.request();
    }
}

靜態(tài)代理需要手動地為每個(gè)目標(biāo)類創(chuàng)建代理類,而代理類中的邏輯都是類似的簡單的,為了更簡單地使用代理出現(xiàn)了動態(tài)代理,通過在運(yùn)行期動態(tài)創(chuàng)建接口對象的代理,避免了手動去創(chuàng)建靜態(tài)代理類。
動態(tài)代理是在運(yùn)行時(shí)動態(tài)生成的,即編譯完成后沒有實(shí)際的 class 文件,而是在運(yùn)行時(shí)動態(tài)生成類字節(jié)碼,并加載到 JVM 中。

JDK 原生動態(tài)代理

JDK 提供了 Proxy.newProxyInstance() 來創(chuàng)建動態(tài)代理。實(shí)現(xiàn)步驟如下:

  • 創(chuàng)建被代理的目標(biāo)類及它的接口
  • 創(chuàng)建接口 InvocationHandler 的實(shí)現(xiàn)類,它必須實(shí)現(xiàn) invoke 方法
  • 通過 Proxy 的靜態(tài)方法 newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 創(chuàng)建一個(gè)代理對象
  • 通過代理對象調(diào)用方法,代理的方法會被 InvocationHandler 的實(shí)現(xiàn)類接管
// 實(shí)現(xiàn) InvocationHandler 類,它的 invoke() 方法會接管代理調(diào)用的方法
public class MyInvocationHandler implements InvocationHandler {
    private Object target;
    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理方法前邏輯");
        Object result = method.invoke(this.target, args);
        System.out.println("代理方法后邏輯");
        return result;
    }
}

// JDK 動態(tài)代理測試
public class JDKDynamicProxyClient {
    public static void main(String[] args) {
        RealService realService = new RealService();
        InvocationHandler handler = new MyInvocationHandler(realService);

        IService service = (IService) Proxy.newProxyInstance(
            // 通常是接口類的 ClassLoader
            IService.class.getClassLoader(),
            // 要實(shí)現(xiàn)的接口數(shù)組
            new Class[]{IService.class},
            // 代理用來處理調(diào)用方法的 InvocationHandler
            handler
        );

        // 通過代理類調(diào)用方法
        service.request();
    }
}

原理分析

主要是看 Proxy.newProxyInstance() 方法。

  • Proxy 類的靜態(tài)變量加載時(shí)初始化 proxyClassCache,代理類緩存中初始化代理類的創(chuàng)建工廠為 ProxyClassFactory()
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
  • Proxy.newProxyInstance() 靜態(tài)方法返回代理類對象,它的調(diào)用過程如下:
Proxy.newProxyInstance()  
      Proxy.getProxyClass0()
        ProxyClassFactory.get() #先從代理類緩存中查詢,沒有時(shí)從代理類工廠中創(chuàng)建
            ProxyClassFactory.apply()   #代理類工廠創(chuàng)建
                ProxyGenerator.generateProxyClass()  #代理類生成器,生成代理類代碼
            ProxyClassFactory.defineClass0()    #加載生成的 class 文件到 jvm,java 接著就能調(diào)用了

最終生成的代理類關(guān)鍵代碼如下:

// 代理類繼承 Proxy 類,并實(shí)現(xiàn) IService 接口
// 代理類名稱固定位 "com.sun.proxy.$Proxy"+自增數(shù)字
public final class com.sun.proxy.$Proxy0 extends Proxy implements IService {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    // 構(gòu)造函數(shù)中設(shè)置代理類的 InvocationHandler
    public com.sun.proxy.$Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    // ...

    public final void request() throws  {
        try {
            // 實(shí)際是 handler 中調(diào)用目標(biāo)類的方法
            super.h.invoke(this, m3, (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"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            // 代理的方法
            m3 = Class.forName("proxy.IService").getMethod("request");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

當(dāng)代理執(zhí)行到 request() 方法時(shí),實(shí)際執(zhí)行的是 InvocationHandler 的 invoke() 方法,并在方法中通過反射調(diào)用目標(biāo)類的真實(shí)方法。

JDK 動態(tài)代理要求被代理的對象必須有對應(yīng)的一個(gè)或多個(gè)接口,不能給原類生成動態(tài)代理類。

CGLIB 動態(tài)代理

CGLIB 是一個(gè)強(qiáng)大的高性能代碼生成包,可以在運(yùn)行期轉(zhuǎn)換字節(jié)碼并生成目標(biāo)類的子類。
CGLIB 創(chuàng)建代理類對象是通過 Enhancer 類實(shí)現(xiàn)的,創(chuàng)建代理的主要步驟:

  • 創(chuàng)建被代理的目標(biāo)類
  • 創(chuàng)建 Enchaner 類對象,設(shè)置對象的父類(為被代理的目標(biāo)類)及 Callback 對象,通過 Enchaner 類的 create() 方法創(chuàng)建代理類對象
  • 通過代理對象調(diào)用方法
// 被代理的類不需要實(shí)現(xiàn)接口,但是被代理的方法和類不能是 final
public class RequestService {
    public void request() {
        System.out.println("真正處理請求內(nèi)容");
    }
}

// CGLIB 代理測試
public class CGLIBProxyClient {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(RequestService.class);
        // 這里是一個(gè)匿名類,被代理方法的處理類,需要實(shí)現(xiàn) MethodInterceptor 接口的 intercept 方法
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects,
                MethodProxy methodProxy) throws Throwable {
                System.out.println("真實(shí)方法前邏輯...");
                methodProxy.invokeSuper(o, objects);
                System.out.println("真實(shí)方法后邏輯...");
                return null;
            }
        });
        RequestService requestService = (RequestService) enhancer.create();
        requestService.request();
    }
}

在項(xiàng)目中通過增加下面設(shè)置,可以將生成的動態(tài)類保存到代碼的根目錄

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./");

運(yùn)行創(chuàng)建代理后,就可以在項(xiàng)目根目錄中看到 enhancer.create 創(chuàng)建的動態(tài)類,它的關(guān)鍵代碼如下:

// 繼承目標(biāo)類 RequestService
public class RequestService$$EnhancerByCGLIB$$6a2da525 extends RequestService implements Factory {
    // 重寫了 request() 方法
    public final void request() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (this.CGLIB$CALLBACK_0 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        // enhancer.setCallback() 已經(jīng)設(shè)置 callback,所以這里不為空
        if (var10000 != null) {
            // 調(diào)用 callback 中實(shí)現(xiàn)的 intercept() 方法
            var10000.intercept(this, CGLIB$request$0$Method, CGLIB$emptyArgs, CGLIB$request$0$Proxy);
        } else {
            super.request();
        }
    }
}

在 Callback 中通過 methodProxy.invokeSuper(o, objects); 來調(diào)用目標(biāo)類的方法

public class MethodProxy {
    public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            // 初始化代理類、目標(biāo)類信息
            // fci.f2 為代理類; fci.i2 為代理類中的代理方法的方法下標(biāo)
            init();
            FastClassInfo fci = fastClassInfo;
            // 代理類通過下標(biāo)調(diào)用代理方法
            return fci.f2.invoke(fci.i2, obj, args);
        }
        catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }
}

自動生成的 FastClass 類主要代碼如下:

// 繼承 FastClass 類
public class RequestService$$FastClassByCGLIB$$86d56cc6 extends FastClass {

    // 根據(jù)方法簽名的 hash 值映射方法的下標(biāo)
    public int getIndex(String var1, Class[] var2) {
        switch(var1.hashCode()) {
        // ...
        case 1095692943:
            if (var1.equals("request")) {
                switch(var2.length) {
                case 0:
                    return 0;
                }
            }
        }

        return -1;
    }

    // 代理類實(shí)際調(diào)用的地方,通過代理方法的下標(biāo)調(diào)用目標(biāo)類方法(不是反射方式)
    public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        RequestService var10000 = (RequestService)var2;
        int var10001 = var1;

        try {
            switch(var10001) {
            case 0:
                var10000.request();
                return null;
            case 1:
                return new Boolean(var10000.equals(var3[0]));
            case 2:
                return var10000.toString();
            case 3:
                return new Integer(var10000.hashCode());
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }

        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }
}

最后

有問題,歡迎留言交流

參考內(nèi)容

https://time.geekbang.org/column/article/10076
https://zhuanlan.zhihu.com/p/35144462

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容