Java動態(tài)代理

一、概述

1、什么是代理

代理模式是常見的Java設(shè)計模式,它的設(shè)計是代理類和委托類有相同的接口, 代理類主要負責(zé)為委托類預(yù)處理消息、過濾消息、把消息轉(zhuǎn)發(fā)給委托類,以及時候處理消息。代理類和委托類之間通常會存在關(guān)聯(lián)關(guān)系, 一個代理類的對象和一個委托類的對象關(guān)聯(lián), 代理類的對象本身并不提供真正的服務(wù),而是通過調(diào)用委托類的相應(yīng)方法來提供特定的服務(wù)。

在日常生活中,代理的應(yīng)用也隨處可見,比如中介、代理加盟商等, 本質(zhì)上都算是一種代理。 以便利店為例,它并不直接生產(chǎn)商品,而是將各個工廠生產(chǎn)的產(chǎn)品聚合在一起, 以方便消費者選擇自己需要的東西。在這個過程中, 便利店、工廠、消費者三者之間的關(guān)系如下圖所示:


image.png

在這三者的關(guān)系中,便利店就充當(dāng)了代理的角色, 當(dāng)然這個例子可能不是一目了然, 用現(xiàn)實生活中的房屋租賃或者法律訴訟來描述應(yīng)該更為貼切。

正是因為存在便利店這這個代理,工廠不需要將大量的商品逐個尋找消費者并出售給對方, 消費者購買多種商品時,也不需要尋找多個工廠去一一購買, 通過便利店這一代理角色, 極大的簡化了工廠和消費者之間的供需關(guān)系。

通過便利店的例子可以發(fā)現(xiàn), 代理在某些場景下可以給我們帶來很多遍歷,將復(fù)雜的事情簡單化。生活中如此,代碼中亦是如此。

2、代理模式

代理模式給某個對象提供一個代理對象, 并由代理對象控制對原對象的引用。當(dāng)用戶不適合或不能直接訪問目標(biāo)對象時,代理對象作為用戶和目標(biāo)對象之間的中介。

image.png

代理模式是一種常見的設(shè)計模式, 它有以下特點

  • 代理類和委托類之間存在關(guān)聯(lián)關(guān)系, 一個代理類和一個委托類關(guān)聯(lián)。
  • 代理類并不提供真正服務(wù), 而是去調(diào)用委托類的服務(wù)。
  • 用戶通過調(diào)用代理對象間接的去調(diào)用委托對象的服務(wù)。
  • 代理類主要負責(zé)在委托類被調(diào)用的前后做一些預(yù)處理的工作,比如打印日志、安全檢查等。

代理模式的角色

  • 抽象角色(Subject): 通過接口或抽象類聲明真實主題和代理對象實現(xiàn)的業(yè)務(wù)方法。
  • 真實角色(Real Subject): 實現(xiàn)了抽象主題中的具體業(yè)務(wù),是代理對象所代表的真實對象, 是最終要引用的對象。
  • 代理(Proxy): 提供了和真實對象相同的接口,其內(nèi)部含有對真實對象的引用, 它可以訪問、控制或擴展真實對象的功能。
  • 用戶(User): 使用代理類來進行一些操作。

代理模式的使用場景

  • 隱藏某個類
  • 擴展某個類的功能

優(yōu)點

  • 代理模式在客戶端與被代理對象中間起到一個中介作用和保護被代理對象的作用
  • 代理對象可以擴展目標(biāo)對象的功能
  • 代理模式可以將客戶端和目標(biāo)對象隔離,一定程度上降低系統(tǒng)的耦合性

3、幾種代理模式

Java中存在2種代理模式:靜態(tài)代理動態(tài)代理, 如下圖所示:

image.png

圖中關(guān)于靜態(tài)代理的具體的使用描述不太準(zhǔn)確,靜態(tài)代理也可以代理多個目標(biāo)對象, 但是對于代理類的實現(xiàn)成本比較高, 要求代理類實現(xiàn)和多個目標(biāo)對象相同的接口,造成代理類比較復(fù)雜繁瑣。

4、靜態(tài)代理

靜態(tài)代理時最基礎(chǔ)、最標(biāo)準(zhǔn)的代理模式。以租房為例子:
1、創(chuàng)建一個租房接口

/**
 * 租房接口
 */
public interface IRent {
    void Rent();
}

2、創(chuàng)建一個租房實現(xiàn)類

public class LandLord implements IRent {
    @Override
    public void Rent() {
        System.out.println("房屋出租");
    }
}

3、創(chuàng)建一個租房代理類

public class RentProxy implements IRent {

    private IRent rent;

    public RentProxy(IRent rent){
        this.rent = rent;
    }

    @Override
    public void Rent() {
        seeHouse();
        this.rent.Rent();
        fare();
    }

    void seeHouse(){
        System.out.println("帶客戶去看房");
    }

    void fare(){
        System.out.println("收取中介費");
    }
}

4、創(chuàng)建客戶端

public class RentClient {
    public static void main(String[] args) {
        IRent rent = new LandLord();
        IRent proxy = new RentProxy(rent);
        proxy.Rent();
    }
}

5、運行結(jié)果


image.png

如上例子所示,靜態(tài)代理的使用方式非常簡單, 通過編程中的組合方法可以輕易實現(xiàn)代理類并為被代理對象增強功能。代理模式可以在不修改被代理對象的基礎(chǔ)上,通過代理類,進行一些功能的擴展和增強。值得注意的時,代理類和被代理對象應(yīng)該實現(xiàn)統(tǒng)一接口,或者共同繼承某個類。

靜態(tài)代理需要預(yù)先定義好并且和被代理對象綁定,并且實現(xiàn)和被代理對象一樣的接口,如果業(yè)務(wù)非常龐大,就需要定義大量的代理類,并且如果業(yè)務(wù)增加新的方法,目標(biāo)對象和代理對象都需要同時維護,維護成本比較高。

比如上面的租房的同時,還有搬家的需求,如果我們同時實現(xiàn)一個搬家的代理,有2種方式:
1)創(chuàng)建新的搬家的代理類,好處是代碼簡單易懂,耦合性低,缺點是需要創(chuàng)建并維護一個新的代理類。
2)在租房代理類中同時代理搬家的需求,好處是不需要創(chuàng)建維護新的代理類,缺點是該代理類變得比較復(fù)雜臃腫,同樣維護成本較高。
在業(yè)務(wù)比較簡單的時候,靜態(tài)代理非常適合作為代理模式的生產(chǎn)實現(xiàn),但是隨著代碼量逐漸增長,業(yè)務(wù)變得復(fù)雜, 靜態(tài)代理的缺點就會凸顯出來。

靜態(tài)代理的特點:

  • 代理類需要代碼運行前創(chuàng)建好
  • 代理類需要在是實現(xiàn)時就指定與被代理類相同的接口

總體來說, 靜態(tài)代理實現(xiàn)比較簡單易懂,但是代碼耦合性較高, 維護成本高,業(yè)務(wù)復(fù)雜的場景下不適合使用。

5、動態(tài)代理

代理類在代碼運行時創(chuàng)建的代理稱之為動態(tài)代理。動態(tài)代理中代理類并不是預(yù)先在Java代碼中定義好的,而是運行時由JVM動態(tài)生成,并且可以代理多個目標(biāo)對象。

5.1、動態(tài)代理的原理
動態(tài)代理的本質(zhì)是在字節(jié)碼的加載過程中生成Proxy字節(jié)碼,這樣就具備動態(tài)生成代理類的能力。

類的加載過程

image.png

在運行時期按照class文件的組織規(guī)范生成新的字節(jié)碼就可以生成新的Java類。在Java中有多個類庫可以實現(xiàn)這些功能, 比如ASM, javaasist等。

5.2、Java中動態(tài)代理的實現(xiàn)方式

  • jdk 動態(tài)代理:Java在JDK1.3后引入的動態(tài)代理機制, 可以在運行期動態(tài)的創(chuàng)建代理類, 只支持接口方式, 使用反射實現(xiàn)。

  • cglib 動態(tài)代理:cglib是一個強大、高效的字節(jié)碼生成類庫,底層采用了ASM。cglib通過操縱字節(jié)碼來實現(xiàn)動態(tài)代理,不強制代理對象必須實現(xiàn)接口, 支持通過繼承代理類的子類的方式來完成代理。

  • javaasist 動態(tài)代理:javaasist是一個開源的創(chuàng)建、分析、編輯Java字節(jié)碼的類庫。主要的優(yōu)點在于簡單、快速,使用者不需要了解匯編指令,就可有動態(tài)修改類的結(jié)構(gòu)或者動態(tài)生成類。javaasist自帶動態(tài)代理的實現(xiàn)。

  • javaasist 字節(jié)碼:javaasist支持使用字符串動態(tài)拼接Java源代碼的方式生成class字節(jié)碼來實現(xiàn)動態(tài)代理。

  • ASM字節(jié)碼: ASM是一個Java字節(jié)碼操控框架, 它能夠以二進制形式修改已有類或者動態(tài)生成類。ASM可以直接產(chǎn)生二進制class文件, 也可以在類被加載如JVM虛擬機之前改變類行為。ASM在創(chuàng)建class字節(jié)碼的過程中,操縱的是底層JVM的匯編指令,要求使用者對class的結(jié)構(gòu)和JVM匯編指令比較了解。

5.3、實現(xiàn)方式對比
從易用性上來說:
1、JDK代理的實現(xiàn)方式最簡單, 不過只能代理接口。
2、其次是CGLIB, 使用也很方便, 不過需要額外引入CGLIB的Jar包。
3、Javaasist需要使用字符串拼接Java源代碼, 比較繁瑣。
4、ASM需要手工寫字節(jié)碼, 對個人要求比較高。

總體而言ASM和Javaasist性能最好, JDK和CGLIB使用方便, 生產(chǎn)環(huán)境下如果要兼顧易用性和性能,推薦使用Javaasist, 其他情況下CGLIB比較合適。

參考文檔:https://www.cnblogs.com/bluemilk/p/11397367.html

二、動態(tài)代理

1、JDK 動態(tài)代理

1.1、概述
JDK動態(tài)代理是Java JDK自帶的一個動態(tài)代理實現(xiàn), 位于java.lang.reflect包下。JDK動態(tài)代理主要包含兩個重要的類:

  • java.lang.reflect.Proxy: Proxy負責(zé)生成動態(tài)代理類。Proxy類有一個很重要的方法:Proxy.newProxyInstance(ClassLoader classLoader, Class<?>[] interfaces, InvocationHandler h), 它的功能就是根據(jù)被代理類和InvocationHandler 接口的實現(xiàn)類動態(tài)生成代理類的class字節(jié)碼, 并返回動態(tài)代理類實例。
  • java.lang.reflect.InvocationHandler: InvocationHandler負責(zé)具體的代理方法實現(xiàn)。InvocationHandler接口只有一個方法:invoke(Object proxy, Method method, Object[] args), 該方法利用反射來調(diào)用被代理類的指定方法并返回結(jié)果, 并且在該方法中可以調(diào)用代理類的其他方法在調(diào)用前后執(zhí)行一些其他動作來增強目標(biāo)類的功能, 比如安全檢查、打印日志等等。

創(chuàng)建JDK動態(tài)代理需要先實現(xiàn)InvocationHandler接口, 并重寫其中的invoke方法,具體步驟如下:

  1. 創(chuàng)建一個業(yè)務(wù)接口和業(yè)務(wù)的實現(xiàn)類。
  2. 創(chuàng)建一個類實現(xiàn)InvocationHandler接口并重寫invoke方法。
  3. 調(diào)用Proxy.newProxyInstance方法生成動態(tài)代理類的class字節(jié)碼并返回動態(tài)代理類的實例。
  4. 客戶端調(diào)用動態(tài)代理類的具體業(yè)務(wù)方法。
//1、創(chuàng)建IBusinessService接口, 包含若干業(yè)務(wù)方法someMethod;
//2、創(chuàng)建IBusinessService接口的具體實現(xiàn)類BusinessService;
//3、創(chuàng)建InvocationHandler接口的實現(xiàn)類SomeInvocationHandler, 并重寫invoke方法;

//4.1、創(chuàng)建BusinessService實例;
IBusinessService businessService = new BusinessService();

//4.2、獲得BusinessService實例的類加載器;
ClassLoader classLoader = businessService.getClass().getClassLoader();

//4.3、獲得BusinessService實例實現(xiàn)的接口集合;
Class<?> interfaces = businessService.getClass().getInterfaces();

//4.4、創(chuàng)建InvocationHandler的實例;
SomeInvocationHandler invocationHandler = new SomeInvocationHandler(businessService);

//4.5、調(diào)用Proxy類生成BusinessService的代理類實例;
IBusinessService businessProxy = (IBusinessService) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);

//4.6、調(diào)用代理類的業(yè)務(wù)方法獲取結(jié)果。
businessProxy.someMethod();

JDK動態(tài)代理要求被代理對象必須實現(xiàn)接口, 不支持繼承, 有2點原因:

  1. Proxy.newProxyInstance方法生成的動態(tài)代理類自身已經(jīng)繼承了java.lang.reflect.Proxy類, 而Java是不支持多繼承的。
  2. Proxy.newProxyInstance生成動態(tài)代理類的時候是根據(jù)被代理類實現(xiàn)的接口interfaces來動態(tài)生成方法, 如果被代理類不實現(xiàn)任何接口,它自身的方法是無法填充到動態(tài)代理類中, 生成的動態(tài)代理類就會不包含自身的業(yè)務(wù)方法,導(dǎo)致無法將動態(tài)代理類類型轉(zhuǎn)換為被代理類, 并且生成的動態(tài)代理類無法被調(diào)用。

1.2、應(yīng)用舉例
以前面租房的場景為例, 使用JDK動態(tài)代理來描述它的使用方式:

  1. 創(chuàng)建一個租房的接口IRent.
public interface IRent {
    void Rent();
}
  1. 創(chuàng)建租房接口的實現(xiàn)類RentService.
public class RentService implements IRent {
    @Override
    public void Rent() {
        System.out.println("房屋出租");
    }
}
  1. 創(chuàng)建一個InvocationHandler的實現(xiàn)類LogHandler, 目標(biāo)是增強被代理類的日志行為,在方法執(zhí)行前后打印日志.
public class LogHandler implements InvocationHandler {
    private Object target; // 被代理對象

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

    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        before(); // 事前執(zhí)行方法, 比如打印日志, 參數(shù)校驗
        Object result =  method.invoke(target, objects);
        after(); // 事后執(zhí)行方法, 比如安全檢查
        return result; // 返回執(zhí)行結(jié)果
    }

    void before(){
        System.out.println("開始打印日志");
    }

    void after(){
        System.out.println("完成打印日志");
    }
}
  1. 創(chuàng)建客戶端UserClient, 生成代理對象并輸出結(jié)果.
public class UserClient {
    public static void main(String[] args) {

        System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");

        // 被代理對象
        RentService rentService = new RentService();

        // 被代理對象類加載器
        ClassLoader classLoader = rentService.getClass().getClassLoader();

        // 被代理對象接口集合
        Class<?>[] interfaces = rentService.getClass().getInterfaces();

        // 請求處理器, 處理所有的代理對象上的方法調(diào)用
        LogHandler logHandler = new LogHandler(rentService);

        // 創(chuàng)建代理對象
        IRent rentProxy = (IRent) Proxy.newProxyInstance(classLoader, interfaces, logHandler);

        // 調(diào)用代理的方法
        rentProxy.Rent();
    }
}
  1. 輸出結(jié)果如下:


    image.png

可以看到不僅輸出了被代理對象的結(jié)果, 還分別在上下文輸出了LogHanderbefore()after()的內(nèi)容。

從上面可以看到, JDK動態(tài)代理不需要事先定義好代理類并且和目標(biāo)類綁定, 而是通過實現(xiàn)一個InvocationHandler的實現(xiàn)類,在它的invoke方法中實現(xiàn)對被代理類的調(diào)用,并且增強原有的功能。JDK代理通過調(diào)用Proxy.newProxyInstance()方法來動態(tài)生成一個代理類,并且不需要我們手動去處理動態(tài)代理類和目標(biāo)類實現(xiàn)相同的接口, 以及在動態(tài)代理類中維護接口的業(yè)務(wù)方法, 如果業(yè)務(wù)方法有新增、修改、刪除, JDK動態(tài)代理會自動幫我們實現(xiàn)。

JDK動態(tài)代理還有一個好處, 就是對于同一類型的代理需求只需要實現(xiàn)一個InvocationHandler, 該InvocationHandler不需要和被代理接口綁定, 所以動態(tài)代理可以綁定多個代理類而不需要重新實現(xiàn)。
舉個例子,還是比如我們有一個搬家的需求,我們定義一個搬家的業(yè)務(wù)接口IMove和實現(xiàn)類MoveService:

// 搬家接口
public interface IMove {
    void move();
}
// 搬家實現(xiàn)類
public class MoveService implements IMove{
    @Override
    public void move() {
        System.out.println("搬家");
    }
}

然后,我們在搬家業(yè)務(wù)中也需要日志增強的功能, 這個時候我們是可以復(fù)用之前實現(xiàn)的日志增強代理LogHandler??纯纯蛻舳送瑫r生成租房和搬家的日志增強代理并輸出結(jié)果:

    public static void main(String[] args) {

        System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");

        // 被代理對象
        RentService rentService = new RentService();

        // 被代理對象類加載器
        ClassLoader classLoader = rentService.getClass().getClassLoader();

        // 被代理對象接口集合
        Class<?>[] interfaces = rentService.getClass().getInterfaces();

        // 請求處理器, 處理所有的代理對象上的方法調(diào)用
        LogHandler logHandler = new LogHandler(rentService);

        // 創(chuàng)建代理對象
        IRent rentProxy = (IRent) Proxy.newProxyInstance(classLoader, interfaces, logHandler);

        // 調(diào)用代理的方法
        rentProxy.Rent();

        IMove moveProxy = (IMove) Proxy.newProxyInstance(MoveService.class.getClassLoader(), MoveService.class.getInterfaces(),
                new LogHandler(new MoveService()));
        moveProxy.move();


    }

}

image.png

可以看到在UserClient中,我們使用LogHandler同時生成了RentServiceMoveService的動態(tài)代理類,它們2個的動態(tài)代理實例被調(diào)用過程中都輸出了LogHandler的日志內(nèi)容。JDK動態(tài)代理的這一特性非常方便我們實現(xiàn)一些通用的功能比如參數(shù)檢查、安全檢查、日志輸出等等。

JDK動態(tài)代理的使用也很簡單明了,基本和JDK靜態(tài)代理差別不大, 只是將一對一實現(xiàn)的靜態(tài)代理類替換成了InvocationHandler。

其次,JDK動態(tài)代理在運行期間還可以將動態(tài)生成的代理類字節(jié)碼保存到本地, 這樣我們就可以查看動態(tài)生成的代理類源碼。保存的方式有2種:

  1. 設(shè)置JVM系統(tǒng)變量jdk.proxy.ProxyGenerator.saveGeneratedFilestrue:
System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");

上面是JDK11的設(shè)置方式, JDK8的設(shè)置方式如下:

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

生成的代理類字節(jié)碼保存在workplace/project/com/sun/proxy目錄下:

image.png

  1. 調(diào)用java.lang.reflect.ProxyGeneratorgeneratedProxyClass方法生成字節(jié)碼并寫入本地(JDK11中該類已經(jīng)變成包類可見, 不能外部使用)。

JDK動態(tài)代理生成的class字節(jié)碼內(nèi)容如下:

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

package com.sun.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import org.good.proxy.IRent;

public final class $Proxy0 extends Proxy implements IRent {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    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 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 void Rent() throws  {
        try {
            super.h.invoke(this, m3, (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"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("org.good.proxy.IRent").getMethod("Rent");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

可以看到生成的代理類命名為$ProxyNumber, 是因為Proxy類中維護了一個全局的AtomicLong(JDK8)計數(shù)器。該$Proxy0代理類繼承了java.lang.reflect.Proxy類并且實現(xiàn)了被代理類的接口, 這也是JDK動態(tài)代理為什么只支持接口代理的一個原因, 因為Java不支持多繼承。

同時可以看到$Proxy0代理類中, 將InvocationHandler實例作為唯一的構(gòu)造器參數(shù)輸入, 在每個方法的具體實現(xiàn)中都是調(diào)用InvocationHandlerinvoke方法來調(diào)用具體的業(yè)務(wù)方法進行請求代理。而InvocationHandlerinvoke方法實際是通過反射來實現(xiàn)的, 它的輸入?yún)?shù)分別是動態(tài)代理類自身、被代理的方法、方法參數(shù), 返回類型和業(yè)務(wù)接口的返回類型一致。

    public final void Rent() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
 @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        before(); // 事前執(zhí)行方法, 比如打印日志, 參數(shù)校驗
        Object result =  method.invoke(target, objects);
        after(); // 事后執(zhí)行方法, 比如安全檢查
        return result; // 返回執(zhí)行結(jié)果
    }

其次,生成的動態(tài)代理類包含多個java.lang.reflect.Method對象:

  private static Method m1;
  private static Method m2;
  private static Method m3;
  private static Method m0;

這些Method對象中,除過包含業(yè)務(wù)接口定義的業(yè)務(wù)方法, 還包含了三個重要方法, 分別是java.lang.Objectequals()、hashCode()、toString()方法。

image.png

總結(jié)而言,JDK動態(tài)代理的特點如下:

  1. 需要實現(xiàn)InvocationHandler接口, 并重寫invoke方法。
  2. 被代理類需要實現(xiàn)接口, 它不支持繼承。
  3. JDK 動態(tài)代理類不需要事先定義好, 而是在運行期間動態(tài)生成。
  4. JDK 動態(tài)代理不需要實現(xiàn)和被代理類一樣的接口, 所以可以綁定多個被代理類。

1.3、實現(xiàn)原理
JDK動態(tài)代理的主要實現(xiàn)原理為反射, 它通過反射在運行期間動態(tài)生成代理類, 并且通過反射調(diào)用被代理類的實際業(yè)務(wù)方法。

java.lang.reflect.ProxynewProxyInstance方法入手我們看一下JDK動態(tài)代理的實現(xiàn)源碼(以JDK11為例)。

  1. Proxy 的成員:
public class Proxy implements Serializable {
    private static final long serialVersionUID = -2222568056686623797L;
    private static final Class<?>[] constructorParams = new Class[]{InvocationHandler.class};
    private static final ClassLoaderValue<Constructor<?>> proxyCache = new ClassLoaderValue();
    protected InvocationHandler h;
    private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class[0];
    private static final String PROXY_PACKAGE_PREFIX = "com.sun.proxy";

    private Proxy() {
    }

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

   /**
   *
   */
}

Proxy類的主要成員變量或類變量為:

  • Class<?>[] constructorParams: 生成的代理類的構(gòu)造器參數(shù), 只有一個InvocationHandler, 從生成的$Proxy0 字節(jié)碼可以看到, 動態(tài)生成的代理類的構(gòu)造器確實只有一個參數(shù)就是InvocationHandler。
  • ClassLoaderValue<Constructor<?>> proxyCache: Proxy類的本地緩存,Key存儲的是被代理類接口的集合, Value存的是生成的動態(tài)代理類的構(gòu)造器實例。
  • InvocationHandler h: Proxy類的唯一成員變量, 作為構(gòu)造器的參數(shù)傳入, 不允許為空。

Proxy類的構(gòu)造函數(shù)只有一個InvocationHandler,因為生成的動態(tài)代理類會繼承Proxy

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

2.Proxy.newProxyInstance()方法

    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {
        Objects.requireNonNull(h);
        Class<?> caller = System.getSecurityManager() == null ? null : Reflection.getCallerClass();
        Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
        return newProxyInstance(caller, cons, h);
    }

newProxyInstance方法的參數(shù)為:

  • ClassLoader: 被代理類的類加載器,用來判斷訪問權(quán)限以及生成的代理類的包路徑。
  • Class<?> interfaces: 被代理類實現(xiàn)的接口集合, 用來生成代理類中的業(yè)務(wù)方法。
  • InvocationHandler: 代理業(yè)務(wù)邏輯的處理handler, 用來作為Proxy的構(gòu)造器參數(shù)并傳遞給代理類。

newProxyInstance方法的邏輯比較簡單:

  • 獲取代理類的構(gòu)造器實例Constructor.
  • 利用構(gòu)造器實例和InvocationHandler的實例生成代理類實例.

需要注意必須實現(xiàn)InvocationHandler, 而不能傳空。

  1. Proxy.getProxyConstructor方法
 private static Constructor<?> getProxyConstructor(Class<?> caller, ClassLoader loader, Class<?>... interfaces) {
        if (interfaces.length == 1) {
            Class<?> intf = interfaces[0];
            if (caller != null) {
                checkProxyAccess(caller, loader, intf);
            }

            return (Constructor)proxyCache.sub(intf).computeIfAbsent(loader, (ld, clv) -> {
                return (new Proxy.ProxyBuilder(ld, (Class)clv.key())).build();
            });
        } else {
            Class<?>[] intfsArray = (Class[])interfaces.clone();
            if (caller != null) {
                checkProxyAccess(caller, loader, intfsArray);
            }

            List<Class<?>> intfs = Arrays.asList(intfsArray);
            return (Constructor)proxyCache.sub(intfs).computeIfAbsent(loader, (ld, clv) -> {
                return (new Proxy.ProxyBuilder(ld, (List)clv.key())).build();
            });
        }
    }

該方法主要做了兩件事:

  • proxyCache中獲取被代理類的動態(tài)代理類構(gòu)造器, 鍵值是由被代理類實現(xiàn)的接口集合生成的AbstractClassLoaderValue.SubK實例。
  • 如果從緩存中獲取不到, 則通過Proxy.ProxyBuilder動態(tài)創(chuàng)建動態(tài)代理類的class字節(jié)碼并生成構(gòu)造器。
  1. Proxy.ProxyBuilder類成員:
  private static final class ProxyBuilder {
        private static final Unsafe UNSAFE = Unsafe.getUnsafe();
        private static final String proxyClassNamePrefix = "$Proxy";
        private static final AtomicLong nextUniqueNumber = new AtomicLong();
        private static final ClassLoaderValue<Boolean> reverseProxyCache = new ClassLoaderValue();
        private static final String DEBUG = GetPropertyAction.privilegedGetProperty("jdk.proxy.debug", "");
        private final List<Class<?>> interfaces;
        private final Module module;
        private static final ClassLoaderValue<Module> dynProxyModules = new ClassLoaderValue();
        private static final AtomicInteger counter = new AtomicInteger();

        ProxyBuilder(ClassLoader loader, List<Class<?>> interfaces) {
            if (!VM.isModuleSystemInited()) {
                throw new InternalError("Proxy is not supported until module system is fully initialized");
            } else if (interfaces.size() > 65535) {
                throw new IllegalArgumentException("interface limit exceeded: " + interfaces.size());
            } else {
                Set<Class<?>> refTypes = referencedTypes(loader, interfaces);
                validateProxyInterfaces(loader, interfaces, refTypes);
                this.interfaces = interfaces;
                this.module = mapToModule(loader, interfaces, refTypes);

                assert Proxy.getLoader(this.module) == loader;

            }
          }

          ProxyBuilder(ClassLoader loader, Class<?> intf) {
            this(loader, Collections.singletonList(intf));
          }

}

比較重要的成員變量:

  • proxyClassNamePrefix: 生成動態(tài)代理類類名的前綴。
  • nextUniqueNumvber: 生成動態(tài)代理類類名的后綴。
    比如$Proxy0、$Proxy1, 根據(jù)運行時Proxy動態(tài)創(chuàng)建的代理類數(shù)量決定。
  • ClassLoader: 類加載器, 用來判斷包路徑和訪問權(quán)限, 作為構(gòu)造器參數(shù)傳入。
  • List<Class<?>> interfaces: 被代理類的接口集合,作為構(gòu)造器參數(shù)傳入。
  1. Proxy.ProxyBuilder.build方法:
    Constructor<?> build() {
            Class proxyClass = defineProxyClass(this.module, this.interfaces);

            final Constructor cons;
            try {
                cons = proxyClass.getConstructor(Proxy.constructorParams);
            } catch (NoSuchMethodException var4) {
                throw new InternalError(var4.toString(), var4);
            }

            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
            return cons;
        }

build方法主要做2件事情:

  • 生成動態(tài)代理類的class字節(jié)碼。
  • 根據(jù)class字節(jié)碼生成動態(tài)代理類的構(gòu)造器實例。
  1. Proxy.ProxyBuilder.defineProxyClass方法:
private static Class<?> defineProxyClass(Module m, List<Class<?>> interfaces) {
           String proxyPkg = null;
           int accessFlags = 17;
           Iterator var4 = interfaces.iterator();

           while(var4.hasNext()) {
               Class<?> intf = (Class)var4.next();
               int flags = intf.getModifiers();
               if (!Modifier.isPublic(flags)) {
                   accessFlags = 16;
                   String pkg = intf.getPackageName();
                   if (proxyPkg == null) {
                       proxyPkg = pkg;
                   } else if (!pkg.equals(proxyPkg)) {
                       throw new IllegalArgumentException("non-public interfaces from different packages");
                   }
               }
           }

           if (proxyPkg == null) {
               proxyPkg = m.isNamed() ? "com.sun.proxy." + m.getName() : "com.sun.proxy";
           } else if (proxyPkg.isEmpty() && m.isNamed()) {
               throw new IllegalArgumentException("Unnamed package cannot be added to " + m);
           }

           if (m.isNamed() && !m.getDescriptor().packages().contains(proxyPkg)) {
               throw new InternalError(proxyPkg + " not exist in " + m.getName());
           } else {
               long num = nextUniqueNumber.getAndIncrement();
               String proxyName = proxyPkg.isEmpty() ? "$Proxy" + num : proxyPkg + "." + "$Proxy" + num;
               ClassLoader loader = Proxy.getLoader(m);
               trace(proxyName, m, loader, interfaces);
               byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, (Class[])interfaces.toArray(Proxy.EMPTY_CLASS_ARRAY), accessFlags);

               try {
                   Class<?> pc = UNSAFE.defineClass(proxyName, proxyClassFile, 0, proxyClassFile.length, loader, (ProtectionDomain)null);
                   reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);
                   return pc;
               } catch (ClassFormatError var10) {
                   throw new IllegalArgumentException(var10.toString());
               }
           }
       }

defineProxyClass方法的執(zhí)行過程如下:

  • 生成代理類包路徑:判斷被代理類實現(xiàn)的接口中, 是否存在非public修飾的接口, 如果有則設(shè)置代理類的包路徑為非public接口的包路徑, 因為如果是非public修飾的只允許包內(nèi)可訪問, 并且如果存在多個非public接口,還需要判斷它們是否位于同一個包下面, 否則會出現(xiàn)訪問權(quán)限問題。如果包路徑為空,則設(shè)置包路徑為默認(rèn)的com.sun.proxy。
  • 生成代理類類名:根據(jù)nextUniqueNumber獲取下一位數(shù)值并拼接類名。
String proxyName = proxyPkg.isEmpty() ? "$Proxy" + num : proxyPkg + "." + "$Proxy" + num;
  • 生成代理類class字節(jié)碼: 調(diào)用ProxyGenerator.generateProxyClass方法生成代理類的字節(jié)碼byte[] proxyClassFile。參數(shù)是類名和接口Class 集合。
  • 生成代理類java.lang.Class對象: 調(diào)用系統(tǒng)方法使用class字節(jié)碼生成對應(yīng)的java.lang.Class對象。

defineProxyClass方法的返回為java.lang.Class, 是動態(tài)生成的代理類Class實例。

7.ProxyGenerator類:

class ProxyGenerator {
    private static final int CLASSFILE_MAJOR_VERSION = 49;
    private static final int CLASSFILE_MINOR_VERSION = 0;
    private static final int CONSTANT_UTF8 = 1;
    private static final int CONSTANT_UNICODE = 2;
    private static final int CONSTANT_INTEGER = 3;
    private static final int CONSTANT_FLOAT = 4;
    private static final int CONSTANT_LONG = 5;
    private static final int CONSTANT_DOUBLE = 6;
    private static final int CONSTANT_CLASS = 7;
    private static final int CONSTANT_STRING = 8;
    private static final int CONSTANT_FIELD = 9;
    private static final int CONSTANT_METHOD = 10;
    private static final int CONSTANT_INTERFACEMETHOD = 11;
    private static final int CONSTANT_NAMEANDTYPE = 12;
    private static final int ACC_PUBLIC = 1;
    private static final int ACC_PRIVATE = 2;
    private static final int ACC_STATIC = 8;
    private static final int ACC_FINAL = 16;
    private static final int ACC_SUPER = 32;
    private static final int opc_aconst_null = 1;
    private static final int opc_iconst_0 = 3;
    private static final int opc_bipush = 16;
    private static final int opc_sipush = 17;
    private static final int opc_ldc = 18;
    private static final int opc_ldc_w = 19;
    private static final int opc_iload = 21;
    private static final int opc_lload = 22;
    private static final int opc_fload = 23;
    private static final int opc_dload = 24;
    private static final int opc_aload = 25;
    private static final int opc_iload_0 = 26;
    private static final int opc_lload_0 = 30;
    private static final int opc_fload_0 = 34;
    private static final int opc_dload_0 = 38;
    private static final int opc_aload_0 = 42;
    private static final int opc_astore = 58;
    private static final int opc_astore_0 = 75;
    private static final int opc_aastore = 83;
    private static final int opc_pop = 87;
    private static final int opc_dup = 89;
    private static final int opc_ireturn = 172;
    private static final int opc_lreturn = 173;
    private static final int opc_freturn = 174;
    private static final int opc_dreturn = 175;
    private static final int opc_areturn = 176;
    private static final int opc_return = 177;
    private static final int opc_getstatic = 178;
    private static final int opc_putstatic = 179;
    private static final int opc_getfield = 180;
    private static final int opc_invokevirtual = 182;
    private static final int opc_invokespecial = 183;
    private static final int opc_invokestatic = 184;
    private static final int opc_invokeinterface = 185;
    private static final int opc_new = 187;
    private static final int opc_anewarray = 189;
    private static final int opc_athrow = 191;
    private static final int opc_checkcast = 192;
    private static final int opc_wide = 196;
    private static final String superclassName = "java/lang/reflect/Proxy";
    private static final String handlerFieldName = "h";
    private static final boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction("jdk.proxy.ProxyGenerator.saveGeneratedFiles"));
    private static Method hashCodeMethod;
    private static Method equalsMethod;
    private static Method toStringMethod;
    private String className;
    private Class<?>[] interfaces;
    private int accessFlags;
    private ProxyGenerator.ConstantPool cp = new ProxyGenerator.ConstantPool();
    private List<ProxyGenerator.FieldInfo> fields = new ArrayList();
    private List<ProxyGenerator.MethodInfo> methods = new ArrayList();
    private Map<String, List<ProxyGenerator.ProxyMethod>> proxyMethods = new HashMap();
    private int proxyMethodCount = 0;

    private ProxyGenerator(String className, Class<?>[] interfaces, int accessFlags) {
        this.className = className;
        this.interfaces = interfaces;
        this.accessFlags = accessFlags;
    }

}

可以看到ProxyGenerator類的成員除過包含類名className和類的接口集合interfaces外, 還包含3個Method對象:

 private static Method hashCodeMethod;
 private static Method equalsMethod;
 private static Method toStringMethod;

這3個方法是多有JDK 動態(tài)代理生成的代理類都會包含的方法。

  1. ProxyGenerator.generateProxyClass方法:
 static byte[] generateProxyClass(final String name, Class<?>[] interfaces, int accessFlags) {
        ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
        final byte[] classFile = gen.generateClassFile();
        if (saveGeneratedFiles) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    try {
                        int i = name.lastIndexOf(46);
                        Path path;
                        if (i > 0) {
                            Path dir = Path.of(name.substring(0, i).replace('.', File.separatorChar));
                            Files.createDirectories(dir);
                            path = dir.resolve(name.substring(i + 1, name.length()) + ".class");
                        } else {
                            path = Path.of(name + ".class");
                        }

                        Files.write(path, classFile, new OpenOption[0]);
                        return null;
                    } catch (IOException var4) {
                        throw new InternalError("I/O exception saving generated file: " + var4);
                    }
                }
            });
        }

        return classFile;
    }

該方法主要做2件事:

  • 調(diào)用generateClassFile方法生成class字節(jié)碼。
  • 判斷是否需要將class字節(jié)碼寫入磁盤。
  1. ProxyGenerator.generateClassFile方法:
  private byte[] generateClassFile() {
        this.addProxyMethod(hashCodeMethod, Object.class);
        this.addProxyMethod(equalsMethod, Object.class);
        this.addProxyMethod(toStringMethod, Object.class);
        Class[] var1 = this.interfaces;
        int var2 = var1.length;

        int var3;
        Class intf;
        for(var3 = 0; var3 < var2; ++var3) {
            intf = var1[var3];
            Method[] var5 = intf.getMethods();
            int var6 = var5.length;

            for(int var7 = 0; var7 < var6; ++var7) {
                Method m = var5[var7];
                if (!Modifier.isStatic(m.getModifiers())) {
                    this.addProxyMethod(m, intf);
                }
            }
        }

        Iterator var11 = this.proxyMethods.values().iterator();

        List sigmethods;
        while(var11.hasNext()) {
            sigmethods = (List)var11.next();
            checkReturnTypes(sigmethods);
        }

        Iterator var15;
        try {
            this.methods.add(this.generateConstructor());
            var11 = this.proxyMethods.values().iterator();

            while(var11.hasNext()) {
                sigmethods = (List)var11.next();
                var15 = sigmethods.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.ProxyMethod pm = (ProxyGenerator.ProxyMethod)var15.next();
                    this.fields.add(new ProxyGenerator.FieldInfo(pm.methodFieldName, "Ljava/lang/reflect/Method;", 10));
                    this.methods.add(pm.generateMethod());
                }
            }

            this.methods.add(this.generateStaticInitializer());
        } catch (IOException var10) {
            throw new InternalError("unexpected I/O Exception", var10);
        }

        if (this.methods.size() > 65535) {
            throw new IllegalArgumentException("method limit exceeded");
        } else if (this.fields.size() > 65535) {
            throw new IllegalArgumentException("field limit exceeded");
        } else {
            this.cp.getClass(dotToSlash(this.className));
            this.cp.getClass("java/lang/reflect/Proxy");
            var1 = this.interfaces;
            var2 = var1.length;

            for(var3 = 0; var3 < var2; ++var3) {
                intf = var1[var3];
                this.cp.getClass(dotToSlash(intf.getName()));
            }

            this.cp.setReadOnly();
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            DataOutputStream dout = new DataOutputStream(bout);

            try {
                dout.writeInt(-889275714);
                dout.writeShort(0);
                dout.writeShort(49);
                this.cp.write(dout);
                dout.writeShort(this.accessFlags);
                dout.writeShort(this.cp.getClass(dotToSlash(this.className)));
                dout.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
                dout.writeShort(this.interfaces.length);
                Class[] var17 = this.interfaces;
                int var18 = var17.length;

                for(int var19 = 0; var19 < var18; ++var19) {
                    Class<?> intf = var17[var19];
                    dout.writeShort(this.cp.getClass(dotToSlash(intf.getName())));
                }

                dout.writeShort(this.fields.size());
                var15 = this.fields.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.FieldInfo f = (ProxyGenerator.FieldInfo)var15.next();
                    f.write(dout);
                }

                dout.writeShort(this.methods.size());
                var15 = this.methods.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.MethodInfo m = (ProxyGenerator.MethodInfo)var15.next();
                    m.write(dout);
                }

                dout.writeShort(0);
                return bout.toByteArray();
            } catch (IOException var9) {
                throw new InternalError("unexpected I/O Exception", var9);
            }
        }
    }

該方法的主要作用是按照class字節(jié)碼的組織結(jié)構(gòu)構(gòu)建class字節(jié)流,比較復(fù)雜專業(yè), 翻看源碼大致流程為:

  • 構(gòu)造Method成員, 這里包含了hashCode、equalstoString3個方法等。
  • 構(gòu)造Field成員。
  • 構(gòu)造字節(jié)碼輸出流DataOutputStream。
  • stream中先后寫入魔數(shù)、代理類訪問控制符、代理類、java.lang.Proxy、接口、成員變量、方法變量等等。

10.InvocationHandler接口:

public interface InvocationHandler {
    Object invoke(Object var1, Method var2, Object[] var3) throws Throwable;
}

InvocationHandler 接口比較簡單, 只要一個方法invoke,由業(yè)務(wù)方自行實現(xiàn)業(yè)務(wù)邏輯。實現(xiàn)的方式是通過Method反射來實現(xiàn)真實的業(yè)務(wù)方法調(diào)用。

綜上可以看到,Proxy類的主要作用是動態(tài)生成代理類 Class實例,真實的業(yè)務(wù)代碼注入都在InvocationHandler中實現(xiàn), 業(yè)務(wù)方需要重寫invoke方法完成真正的業(yè)務(wù)調(diào)用,并實現(xiàn)功能增強。JDK總臺代理主要是通過反射的方式來動態(tài)生成字節(jié)碼從而實現(xiàn)動態(tài)代理。

1.4、存在的問題
JDK動態(tài)代理雖然實現(xiàn)很簡單, 但是要求被代理類必須實現(xiàn)方法, 而不支持繼承,因為生成的代理類已經(jīng)繼承了java.lang.reflect.Proxy類, Java不支持多繼承。

如果我們定義一個被代理類,該類不實現(xiàn)任何接口會怎樣?下面我們做一個實驗。

  1. 定義一個 RunService不實現(xiàn)任何接口。
public class RunService {
    public void run(){
        System.out.println("跑步");
    }
}
  1. InvocationHandler繼續(xù)使用上面的LogHandler。
public class LogHandler implements InvocationHandler {
    private Object target; // 被代理對象

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

    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        before(); // 事前執(zhí)行方法, 比如打印日志, 參數(shù)校驗
        Object result =  method.invoke(target, objects);
        after(); // 事后執(zhí)行方法, 比如安全檢查
        return result; // 返回執(zhí)行結(jié)果
    }

    void before(){
        System.out.println("開始打印日志");
    }

    void after(){
        System.out.println("完成打印日志");
    }
}
  1. Client端通過Proxy.newInstance獲取代理類實例,并調(diào)用run方法。
public class UserClient {
    public static void main(String[] args) {

       System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");

        System.out.println("開始測試...");
        RunService runService = new RunService();
        ClassLoader classLoader = runService.getClass().getClassLoader();
        Class<?>[] interfaces = runService.getClass().getInterfaces();
        LogHandler logHandler = new LogHandler(runService);

        RunService runProxy = (RunService)Proxy.newProxyInstance(classLoader, interfaces, logHandler);
        runProxy.run();
        System.out.println("結(jié)束測試...");

    }
}

此處IDEA中代碼并沒有報錯, 編譯也是可以通過的。

  1. 執(zhí)行UserClient看看是否可以執(zhí)行成功。
    image.png

可以看到第40行報錯了, 錯誤內(nèi)容為生成的動態(tài)代理類$Proxy0無法轉(zhuǎn)換為RunService類型。經(jīng)過Debug Proxy.newProxyInstance方法確實生成了代理類對象, 那么是為什么呢,我們來看看生成的class文件:

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

package com.sun.proxy;

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

public final class $Proxy0 extends Proxy {
    private static Method m1;
    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 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"));
            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());
        }
    }
}

可以看到由于RunService沒有實現(xiàn)任何接口, 所以生成的代理類也沒有實現(xiàn)任任何接口, 并且生成的代理類只有3個默認(rèn)的方法, 并不包含期待的run方法。由于$Proxy0類和RunService沒有實現(xiàn)共同的接口也沒有繼承共同的父類, 所以它們無法相互轉(zhuǎn)換, 就會出現(xiàn)上圖中轉(zhuǎn)換失敗, 由于代理類是運行時創(chuàng)建,所以代理編譯器并不會報出錯誤。

另外,ProxyGenerator.generateProxyClass生成代理類字節(jié)碼的時候, 是根據(jù)被代理類繼承的接口來構(gòu)造Method, 所以代理類自身的方法并不會被添加到代理類中, 如果被代理類不實現(xiàn)任何方法, 雖然可以成功生成代理類, 但是生成的代理類只包含3個默認(rèn)方法。

綜上上述, JDK動態(tài)代理必須通過接口實現(xiàn), 主要原因如下:

  • 生成的代理類自身已經(jīng)繼承java.lang.reflect.Proxy類,由于Java不支持多繼承, 所以被代理類不能繼承任何類, 負責(zé)無法實現(xiàn)。
  • 如果被代理類不實現(xiàn)任何接口, 生成的代理類和被代理類無共同接口或父類,無法相互轉(zhuǎn)換,且不包含被代理類自有的方法。
  • 如果代理類自身已經(jīng)繼承了其他父類, 也可以成功生成代理類,但是生成的代理類和不實現(xiàn)任何接口的效果一樣。

1.5、總結(jié)
JDK 動態(tài)代理的實現(xiàn)原理是通過反射 來動態(tài)創(chuàng)建或修改字節(jié)碼來生成動態(tài)代理類。它包含2個核心類庫:

  • java.lang.reflect.InvocationHandler: 負責(zé)具體的業(yè)務(wù)代碼注入。
  • java.lang.reflect.Proxy: 負責(zé)動態(tài)生成代理類。

JDK 動態(tài)代理的主要優(yōu)點為:

  • 使用簡單、維護成本低。
  • Java原生支持,不需要任何依賴。
  • 解決了靜態(tài)代理存在的多數(shù)問題。

主要缺點為:

  • 由于使用反射,性能會比較差。
  • 只支持接口實現(xiàn),不支持繼承, 不滿足所有業(yè)務(wù)場景。

2、 CGLIB

2.1、概述

1、什么是CGLIB?

CGLIB是一個強大的、高性能的代碼生成庫。它可以在運行期擴展Java類和接口,其被廣泛應(yīng)用于AOP框架中(Spring、dynaop)中, 用以提供方法攔截。Hibernate作為一個比較受歡迎的ORM框架, 用它來實現(xiàn)PO字節(jié)碼的動態(tài)生成。 CGLIBJDK動態(tài)代理更強的地方在于它不僅可以接管Java接口, 還可以接管普通類的方法。

CGLIB的底層原理是使用的ASM來操縱字節(jié)碼。它作為一個開源項目, 代碼被托管在github上, 項目地址是:https://github.com/cglib/cglib。它通過操縱字節(jié)碼生成被代理類的一個或多個子類來實現(xiàn)對被代理類的代理。

CGLIB和一些常見框架和語言(主要是Java)的關(guān)系如下:

image.png

  • 最底層的是字節(jié)碼, 字節(jié)碼是Java為了保證"一次編譯、到處運行"而產(chǎn)生的一種虛擬指令格式, 例如iload_0, iconst_1等等。
  • 字節(jié)碼上面一層的是ASMASM是Java中可以直接操縱字節(jié)碼的一個類庫, 使用它需要對Java字節(jié)碼、class組織結(jié)構(gòu)比較熟悉。
  • 位于ASM之上的是CGLIB、Groovy等框架和語言,它們通過ASM框架動態(tài)生成字節(jié)碼來變相執(zhí)行Java程序。這也說明Java并不是唯一可被JVM執(zhí)行的語言, 只要可以生成字節(jié)碼,JVM并不關(guān)心字節(jié)碼的來源
  • 位于CGLIB等框架之上的就是Spring、Hibernate等這些框架了。

2、為什么使用CGLIB?
CGLIB代理主要通過對字節(jié)碼操作, 為對象引入不同的子類,以控制對象的訪問。CGLIB主要解決JDK動態(tài)代理要求被代理類必須繼承接口的缺點,它可以對非繼承自接口的類進行代理, 同時CGLIB的功能更加強大, 支持更多的定制化功能。

3、如何獲得CGLIB?
CGLIB的源碼維護在github上:https://github.com/cglib/cglib。開發(fā)的時候需要引入cglib的jar包, 可以通過maven來引入:

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>cglib-version</version>
</dependency>

4、CGLIB的源碼結(jié)構(gòu)
cglib-3.3.0為例, 它主要包含以下幾個模塊:

image.png

  • net.sf.cglib.beans: JavaBean的工具類。
  • net.sf.cglib.core:底層字節(jié)碼處理類, 大部分和ASM有關(guān)系。
  • net.sf.cglib.proxy:實現(xiàn)創(chuàng)建代理和方法攔截器的類。
  • net.sf.cglib.reflect:實現(xiàn)Fast反射和C#風(fēng)格的類。
  • net.sf.cglib.transform:編譯器或運行期類和類文件的轉(zhuǎn)換工具。
  • net.sf.cglib.util: 其他工具類。

CGLIB的底層主要是通過ASM來操縱字節(jié)碼實現(xiàn)類的動態(tài)生成。對于創(chuàng)建動態(tài)代理, 大部分情況下, 只需要使用net.sf.cglib.proxy包的一部分API即可。

CGLIB的動態(tài)關(guān)聯(lián)圖如下:


image.png

可以看到, 它核心的API有兩個:

  • net.sf.cglib.Enhancer: 代碼增強器, 用來動態(tài)生成目標(biāo)類的代理類進行代理。
  • net.sf.cglib.Callback: 目標(biāo)類被調(diào)用時, 被攔截后調(diào)用的回調(diào), 用來實現(xiàn)具體的回調(diào)邏輯, 增強業(yè)務(wù)代碼, 比如打印日志、安全檢查等等。

5、CGLIB的重要API

  • net.sf.cglib.proxy.Enhancer: CGLIB中的字節(jié)碼增強器, 類似于JDK里的java.lang.reflect.Proxy, 它既可以代理接口, 也可以代理普通的Java類, 它通過創(chuàng)建被代理類的子類并攔截所有的方法調(diào)用(包括Object#toString()Object#hashCode), 但是它不能攔截final修飾的方法(如Object#getClass()), 也不能代理final修飾的類。
    Enhancer類包含幾個核心的同名重載方法create來創(chuàng)建代理類, 調(diào)用create方法之前需要預(yù)先設(shè)置被代理類setSuperClass、設(shè)置代理回調(diào)setCallbacksetCallbacks等等。

  • net.sf.cglib.proxy.Callback: CGLIB中的回調(diào), 作用類似于JDK里的java.lang.reflect.InvocationHandler, 負責(zé)業(yè)務(wù)方法的實現(xiàn)和增強。 它的回調(diào)時機是被代理類的方法被調(diào)用的時候, 即被代理類的方法被調(diào)用時, Callback的實現(xiàn)邏輯就會被調(diào)用??梢酝ㄟ^Enhancer#setCallback()Enhancer#setCallbacks()設(shè)置Callback, 若設(shè)置了多個Callback, 則會按照設(shè)置的順序進行回調(diào)。它只是一個標(biāo)識接口, CGLIB提供了多個它的子類。

  • net.sf.cglib.proxy.CallbackFilter: CallbackFilter可以實現(xiàn)不同的方法使用不同的回調(diào)方法。它的int accept(Method method)方法, 根據(jù)不同的method返回不同的值i,這個值時回調(diào)集合callbacks中的順序, 就是調(diào)用了callbacks[i]

  • net.sf.cglib.proxy.Dispatcher: Callback的常用子類之一, 主要應(yīng)用在懶加載場景下, 和LazyLoader不同的是每次調(diào)用代理方法時都會調(diào)用到loadObject方法加載被代理對象。

  • net.sf.cglib.proxy.FixedValue: Callback的常用子類之一, 強制一個特定方法返回固定值, 特定場景下非常有用且性能高。

  • net.sf.cglib.proxy.LazyLoader: Callback的常用子類之一, 主要應(yīng)用在懶加載場景下, 它有一個Object loadObject()方法,用來被代理對象懶加載時初始化該對象。 它的特點是當(dāng)被聲明為懶加載的對象已經(jīng)被加載完成后, 下次調(diào)用時, 就會直接使用該對象, 不會再次調(diào)用loadObject()方法進行加載。

  • net.sf.cglib.proxy.MethodInterceptor:Callback的最常用子類, 是最常用的回調(diào)類型。它被設(shè)置為方法回調(diào)時, 當(dāng)調(diào)用代理的方法時, 首先會調(diào)用它的intercept方法,然后調(diào)用被代理對象的目標(biāo)方法, 這個過程中在調(diào)用前后可以做一些業(yè)務(wù)增強的功能。intercept方法有4個參數(shù), 分別是代理對象Object、被代理的方法Method、被代理方法的參數(shù)Object[] objects、被代理方法的代理MethodProxy

  • net.sf.cglib.proxy.ProxyRefDispatcher: Callback的子類之一, 和Dispatcher相同, 不同的是它的loadObject方法支持傳入代理對象。

  • net.sf.cglib.proxy.NoOp: Callback的子類之一, 它代表什么都不做, 直接調(diào)用被代理類的方法。

  • net.sf.cglib.proxy.InterfaceMaker: 顧名思義, 它是一個接口生成器, 它可以根據(jù)指定的類動態(tài)生成一個接口, 該接口包含指定類定義的所有方法。

  • net.sf.cglib.proxy.InvocationHandler: CGLIB中參考JDK動態(tài)代理實現(xiàn)的InvocationHandler, 可以和net.sf.cglib.proxy.Proxy配合替換JDK的java.lang.reflect.Proxyjava.lang.reflect.InvocationHandler, 它同樣也定義了一個invoke方法, 供按照JDK動態(tài)代理的方式實現(xiàn)業(yè)務(wù)代碼的增強修改。

  • net.sf.cglib.proxy:Proxy:CGLIB中參考JDK動態(tài)代理實現(xiàn)的代理生成類, 可以和CGLIB自帶的InvocationHandler配合按照JDK動態(tài)代理規(guī)范實現(xiàn)動態(tài)代理, 它和java.lang.reflect.Proxy一樣支持newProxyInstance方法動態(tài)生成代理類。它的內(nèi)部是調(diào)用Enhancer動態(tài)生成代理類, 并且設(shè)置了一個默認(rèn)的CallbackFilter, 當(dāng)攔截到Object#hashCode、Object#equalsObject#toString3個方法時, 調(diào)用NoOp回調(diào), 啥都不做, 直接調(diào)用父類的方法, 當(dāng)攔截到其他方式時, 調(diào)用用戶重寫的InvocationHandler來實現(xiàn)回調(diào)。

  • net.sf.cglib.proxy.CallbackGenerator: Callback的處理接口, 它是一個接口, 包含2個重要的方法generategenerateStatic, 主要用來實現(xiàn)覆蓋父類的方法, 每一個Callback子接口都有一個對應(yīng)的CallbackGenerator子類。

  • net.sf.cglib.proxy.Factory: CGLIB實現(xiàn)代理所必要實現(xiàn)的接口,用于提供一些工具方法。

6、CGLIB的使用過程
CGLIB的使用, 需要先實現(xiàn)一個Callback回調(diào), 一般常用的是MethodInterceptor子類,它可以滿足大多數(shù)的動態(tài)代理需求, 具體步驟如下:

  1. 創(chuàng)建一個被代理類。
  2. 創(chuàng)建一個類實現(xiàn)CallbackCallback的子接口并重寫業(yè)務(wù)織入的邏輯, 也可以不實現(xiàn), 使用默認(rèn)的NoOp即可, 功能和沒使用動態(tài)代理一樣, 直接調(diào)用被代理類的方法。
  3. 創(chuàng)建一個Enhancer字節(jié)碼增強器對象, 為該對象設(shè)置被代理類(setSuperClass)、回調(diào)setCallback、回調(diào)順序setCallbackFilter等等屬性。
    4、 調(diào)用Enhancer實例的create方法, 動態(tài)生成一個被代理類子類的字節(jié)碼并返回實例。
    5、客戶端調(diào)用動態(tài)代理類的具體業(yè)務(wù)方法, 注意這里調(diào)用的是CGLIB為被代理類生成的一個子類的實例, 而不是被代理類的實例, CGLIB生成的動態(tài)代理類都會繼承被代理類。
//1. 創(chuàng)建被代理類Target

//2. 實現(xiàn)回調(diào)Callback

//3. 初始化Enhancer
Enhancer enhancer = new Enhancer();
// 設(shè)置被代理類
enhancer.setSuperClass(Target.class);
// 設(shè)置回調(diào)
enhancer.setCallBack(new Callback());
enhancer.setCallbacks({newCallback1(), new Callback2()});
// 設(shè)置回調(diào)順序,可以忽略,
// 忽略的話就會對目標(biāo)類所有的方法執(zhí)行相同的回調(diào)。
enhancer.setCallbackFilter(new CallbackFilter());

//4. 獲取代理對象
Target targetProxy = (Target)enhancer.create();

// 5. 客戶端調(diào)用
targetProxy.method();

2.2、應(yīng)用例子

2.3、原理

2.4、存在問題

2.5、總結(jié)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容