Java動(dòng)態(tài)代理解析

Java動(dòng)態(tài)代理的用法如下:

public class Main {

    public static void main(String[] args) throws IOException {
        // 1. 創(chuàng)建Proxy對(duì)象,并強(qiáng)制轉(zhuǎn)換為接口類型
        Test proxy = (Test)Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[]{Test.class}, new InvocationHandler() {  // 2. 創(chuàng)建InvocationHandler對(duì)象,并在invoke中做方法實(shí)現(xiàn)
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                method.invoke(new Test() {
                    @Override
                    public void print() {
                        System.out.println("這里是匿名內(nèi)部類");
                    }
                }, args);
                return null;
            }
        });
        
        // 3. 使用
        proxy.print();
    }
}

interface Test {
    public abstract void print();
}

而上面InvocationHandler的invoke方法中對(duì)接口定義的方法的實(shí)現(xiàn)是通過(guò)接口的匿名內(nèi)部類完成的,當(dāng)然還可以使用其他的方式,例如:

Test proxy = (Test)Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[]{Test.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("這里是代理方法");
                if (method.getName().equals("print")) {
                    System.out.println("你調(diào)用了print方法,來(lái)自于接口");
                } else {
                    System.out.println("你調(diào)用了來(lái)自O(shè)bject的方法,"+method.getName());
                }
                return null;
            }
        });
        proxy.print();

上述展示的在InvocationHandler的invoke方法中對(duì)各個(gè)方法調(diào)用的實(shí)現(xiàn)是通過(guò)if語(yǔ)句判斷調(diào)用的方法,然后進(jìn)行操作的。

關(guān)于Java中的動(dòng)態(tài)代理,其中幾個(gè)關(guān)鍵角色說(shuō)一下:

  • Proxy:官方提供的類,主要使用其newProxyInstance靜態(tài)方法獲取對(duì)接口進(jìn)行實(shí)現(xiàn)的、真正動(dòng)態(tài)代理類對(duì)象;
  • InvocationHandler:方法調(diào)用委派對(duì)象,接口中所定義的API方法,在生成的動(dòng)態(tài)代理類中,全都是分派到該handler的invoke方法,即接口方法的真正實(shí)現(xiàn)邏輯是需要開(kāi)發(fā)者在handler的invoke中進(jìn)行

說(shuō)明:在InvocationHandler的invoke方法中,第一個(gè)參數(shù)proxy就是實(shí)際的代理對(duì)象,而第二個(gè)參數(shù)為前者含有的各個(gè)方法,最后一個(gè)是方法參數(shù)。他們之間的關(guān)系如下:

class Proxy$0 {
 Method m1 = Class.forName("XXX.Proxy$0").getMethod("method1", Class.forName("[java.lang.Object"));
  public Object method1(Object[] args) {
      return handler.invoke(this, m1, args);
  }
}

即傳入的InvocationHandler的第一個(gè)Object參數(shù)為代理類對(duì)象this,第二個(gè)參數(shù)為被調(diào)用的方法Method對(duì)象,第三個(gè)參數(shù)是方法參數(shù)。

因此,InvocationHandler的invoke方法中,不能夠method.invoke(proxy, args),這會(huì)導(dǎo)致無(wú)限死循環(huán)proxy.h.invoke() -> proxy.method.invoke() -> proxy.h.invoke() ...

因此,根據(jù)上述的使用方式,我們有如下兩個(gè)疑問(wèn):

  • 生成的代理類如何持有InvocationHandler對(duì)象的
  • 生成的代理類如何實(shí)現(xiàn)方法的

Proxy通過(guò)一個(gè)靜態(tài)方法newProxyInstance隱去了所有的細(xì)節(jié),我們就來(lái)看看該方法的實(shí)現(xiàn):

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

        final Class<?> caller = System.getSecurityManager() == null
                                    ? null
                                    : Reflection.getCallerClass();

        Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);

        return newProxyInstance(caller, cons, h);
    }

該方法,大體上很簡(jiǎn)單,獲取構(gòu)造函數(shù),然后調(diào)用newProxyInstance方法返回對(duì)象,而newProxyInstance方法內(nèi)部也只是簡(jiǎn)單的回調(diào)cons.newInstance:

    private static Object newProxyInstance(Class<?> caller, // null if no SecurityManager
                                           Constructor<?> cons,
                                           InvocationHandler h) {
        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (caller != null) {
                checkNewProxyPermission(caller, cons.getDeclaringClass());
            }

            return cons.newInstance(new Object[]{h});
        } ....
    }

唉,我們發(fā)現(xiàn),調(diào)用Constructor的newInstance時(shí),傳遞的參數(shù)正式InvocationHandler對(duì)象,這就說(shuō)明了,生成的代理類有一個(gè)構(gòu)造器,該構(gòu)造器接收一個(gè)InvocationHandler對(duì)象為參數(shù)。因此,就來(lái)到了第一個(gè)關(guān)注的問(wèn)題:代理類是如何以及何時(shí)生成這樣一個(gè)構(gòu)造器的?問(wèn)題的答案,一定來(lái)自getProxyConstructor方法,在分析該方法前我們先不關(guān)心這里的參數(shù)caller為何,僅當(dāng)其為null。

    private static Constructor<?> getProxyConstructor(Class<?> caller,
                                                      ClassLoader loader,
                                                      Class<?>... interfaces)
    {
        // optimization for single interface
        if (interfaces.length == 1) {
            Class<?> intf = interfaces[0];
            if (caller != null) {
                checkProxyAccess(caller, loader, intf);
            }
            return proxyCache.sub(intf).computeIfAbsent(
                loader,
                (ld, clv) -> new ProxyBuilder(ld, clv.key()).build()  // build()方法返回的才是Constructor對(duì)象
            );
        } else {
            // interfaces cloned
            final Class<?>[] intfsArray = interfaces.clone();
            if (caller != null) {
                checkProxyAccess(caller, loader, intfsArray);
            }
            final List<Class<?>> intfs = Arrays.asList(intfsArray);
            return proxyCache.sub(intfs).computeIfAbsent(
                loader,
                (ld, clv) -> new ProxyBuilder(ld, clv.key()).build()  // build()方法返回的才是Constructor對(duì)象
            );
        }
    }

實(shí)際上的構(gòu)造器正是通過(guò)ProxyBuilder的build()方法返回的,至于這里的getProxyConstructor方法其他部分到底是做什么的,我們先不管,直接來(lái)看ProxyBuilder.build()方法

// ProxyBuilder.java

        Constructor<?> build() {
            Class<?> proxyClass = defineProxyClass(module, interfaces);
            final Constructor<?> cons;
            try {
                cons = proxyClass.getConstructor(constructorParams);
            } ...
            return cons;
        }

該方法也很簡(jiǎn)單明了,就是創(chuàng)建代理類的Class對(duì)象,然后獲取其構(gòu)造器返回就行了??吹竭@里是不是激動(dòng)了起來(lái),代理類的Class對(duì)象?好家伙,我們明明沒(méi)有寫(xiě)過(guò)一行代理類的源代碼,Java是怎么給我們憑空創(chuàng)建出來(lái)一個(gè)Class對(duì)象的?那么這個(gè)代理類的Class文件長(zhǎng)啥樣呢?咋生成的呢?

關(guān)于上面的代碼,還有一個(gè)要說(shuō)的點(diǎn)是constructorParams是一個(gè)在定義時(shí)就被初始化了的屬性

private static final Class<?>[] constructorParams =
        { InvocationHandler.class };

嗯,不出所料,正印證了前面在調(diào)用構(gòu)造器的newInstance方法時(shí)需要傳入InvocationHandler對(duì)象。

這就引入了問(wèn)題的關(guān)鍵:Java是如何憑空定義一個(gè)代理類的?又是如何生成其Class文件的?搞清楚其中內(nèi)容,自然也就知道了接口中的方法以及構(gòu)造器是如何寫(xiě)的了。因此,關(guān)鍵來(lái)到了defineProxyClass方法:

// ProxyBuilder.java

        private static Class<?> defineProxyClass(Module m, List<Class<?>> interfaces) {
            ....
            /*
             * Generate the specified proxy class.
             */
            byte[] proxyClassFile = PROXY_GENERATOR_V49
                    ? ProxyGenerator_v49.generateProxyClass(proxyName, interfaces, accessFlags)
                    : ProxyGenerator.generateProxyClass(loader, proxyName, interfaces, accessFlags);
            try {
                Class<?> pc = JLA.defineClass(loader, proxyName, proxyClassFile,
                                              null, "__dynamic_proxy__");
                reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);
                return pc;
            } 
        }

該方法先會(huì)處理一些module以及package的信息,但這不是我們所關(guān)心的,直接來(lái)到generateProxyClass方法調(diào)用處,該方法就是生成代理類的地方,我們這里就看第二個(gè)ProxyGenerator吧。

// ProxyGenerator.java
    static byte[] generateProxyClass(ClassLoader loader,
                                     final String name,
                                     List<Class<?>> interfaces,
                                     int accessFlags) {
        ProxyGenerator gen = new ProxyGenerator(loader, name, interfaces, accessFlags);
        final byte[] classFile = gen.generateClassFile();

        ....
        return classFile;
    }

已經(jīng)離答案越來(lái)越近了,生成Class文件的地方就是generateClassFile方法:

// ProxyGenerator.java

private byte[] generateClassFile() {
        visit(V14, accessFlags, dotToSlash(className), null,
                JLR_PROXY, typeNames(interfaces));

        // 添加上hashCode、equals、toString三個(gè)從Object繼承來(lái)的方法
        addProxyMethod(hashCodeMethod);
        addProxyMethod(equalsMethod);
        addProxyMethod(toStringMethod);

        
        for (Class<?> intf : interfaces) {
            for (Method m : intf.getMethods()) {
                if (!Modifier.isStatic(m.getModifiers())) {
                    // 添加上所有想要代理的接口的方法
                    addProxyMethod(m, intf);
                }
            }
        }

        ...
            
        // 憑空生成構(gòu)造器
        generateConstructor();

        for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
            for (ProxyMethod pm : sigmethods) {
                // add static field for the Method object
                visitField(Modifier.PRIVATE | Modifier.STATIC, pm.methodFieldName,
                        LJLR_METHOD, null, null);

                // 為所有上面添加的方法生成方法實(shí)現(xiàn)code
                pm.generateMethod(this, className);
            }
        }

        // 生成初始化塊兒
        generateStaticInitializer();
    
        return toByteArray();
    }

可以看到,generateClassFile思路非常清晰:

  • 添加所有涉及到的方法,并之后generate出方法實(shí)現(xiàn);
  • 為構(gòu)造器generate出實(shí)現(xiàn);
  • generate靜態(tài)初始化塊兒。

這三個(gè)內(nèi)容唯獨(dú)對(duì)方法的處理需要先add,我們就先看看add的到底是啥:

    private final static ProxyMethod hashCodeMethod;
    private final static ProxyMethod equalsMethod;
    private final static ProxyMethod toStringMethod;

    static {
        try {
            hashCodeMethod = new ProxyMethod(Object.class.getMethod("hashCode"), "m0");
            equalsMethod = new ProxyMethod(Object.class.getMethod("equals", Object.class), "m1");
            toStringMethod = new ProxyMethod(Object.class.getMethod("toString"), "m2");
        } catch (NoSuchMethodException e) {
            throw new NoSuchMethodError(e.getMessage());
        }
    }

可以看到,原來(lái)在ProxyGenerator中每個(gè)方法使用一個(gè)ProxyMethod指代,ProxyMethod中首先是包含了對(duì)應(yīng)的真實(shí)的Method對(duì)象,然后緊跟的一個(gè)字符串代表著該方法在代理類對(duì)象中對(duì)應(yīng)的引用的名字,例如在代理類中m0屬性如下定義:

Method m0 = Class.forName("java.lang.Object").getMethod("hashCode");

找到了地方,我們先來(lái)看看是如何為代理類生成構(gòu)造器的代碼的。

生成構(gòu)造器

該功能由generateConstructor()方法實(shí)現(xiàn):

// ProxyGenerator.java    
private void generateConstructor() {
        MethodVisitor ctor = visitMethod(Modifier.PUBLIC, NAME_CTOR,
                MJLR_INVOCATIONHANDLER, null, null);
        ctor.visitParameter(null, 0);
        ctor.visitCode();
        ctor.visitVarInsn(ALOAD, 0);
        ctor.visitVarInsn(ALOAD, 1);
        ctor.visitMethodInsn(INVOKESPECIAL, JLR_PROXY, NAME_CTOR,
                MJLR_INVOCATIONHANDLER, false);
        ctor.visitInsn(RETURN);

        // Maxs computed by ClassWriter.COMPUTE_FRAMES, these arguments ignored
        ctor.visitMaxs(-1, -1);
        ctor.visitEnd();
    }

一頭霧水,這些都是啥啊。不急,我們先來(lái)看看visitMethod方法傳遞的參數(shù)就知道了:

  • Modifier.PUBLIC:唉,這不是public修飾符嗎?
  • NAME_CTOR:是個(gè)常量,內(nèi)容為"<init>",莫名熟悉;
  • MJLR_INVOCATIONHANDLER:也是個(gè)常量,內(nèi)容為"(Ljava/lang/reflect/InvocationHandler;)V"!!

看到上面的三個(gè)參數(shù),大師,我悟了?。∵@三個(gè)拼起來(lái),不就是class文件中構(gòu)造器的descriper嗎?原來(lái),Java的動(dòng)態(tài)代理真的想跳過(guò)源碼編譯的步驟,直接寫(xiě)出一個(gè)class文件來(lái)?。?/p>

看到這里大致明白了,這里的MethodVisitor應(yīng)該是代表著要往class文件中寫(xiě)入一個(gè)Method,而創(chuàng)建MethodVisitor時(shí)根據(jù)傳入的參數(shù)確定下了該Method的描述符以及訪問(wèn)權(quán)限,那么還有一個(gè)重頭戲就是Method的code部分怎么寫(xiě),這些就是上述的各種visitXXX了

實(shí)際上根據(jù)上述幾個(gè)visitXXX的第一個(gè)參數(shù):ALOAD、INVOKESPECIAL、RETURN就能猜出來(lái)了,這寫(xiě)顯然就是JVM標(biāo)準(zhǔn)定義的棧指令描述符啊,因此,后續(xù)調(diào)用visitEnd()方法之前的各種visitXXX方法就是在硬寫(xiě)一個(gè)個(gè)指令完成方法code的書(shū)寫(xiě)。

等等,說(shuō)著說(shuō)著,怎么為代理類生成的構(gòu)造器中使用invokespecial指令回調(diào)了其他類的方法,我們先來(lái)看看回調(diào)的是誰(shuí)的方法:

JLR_PROXY -> private static final String JLR_PROXY = "java/lang/reflect/Proxy";
NAME_CTOR -> private static final String NAME_CTOR = "<init>;
MJLR_INVOCATIONHANDLER -> private static final String MJLR_INVOCATIONHANDLER = "(Ljava/lang/reflect/InvocationHandler;)V";

破案了,原來(lái)生成的代理類繼承自Proxy類,而且構(gòu)造器的實(shí)現(xiàn)就是簡(jiǎn)單的super(InvocationHandler),而我們看看Proxy構(gòu)造器:

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

好小子,分析了半天,讓你Proxy小子給截胡了。

生成普通方法

代理類中方法屬性的命名問(wèn)題

在ProxyGenerator類中直接為hashCode、toString、equals三個(gè)方法創(chuàng)建好了對(duì)應(yīng)的ProxyMethod對(duì)象,并且提到了為其在代理類中的屬性命名為了m0、m1、m2;而其他來(lái)自接口中的方法又是如何命名的呢?而上面顯示,來(lái)自于接口的方法被addProxyMethod的方式添加了進(jìn)來(lái):

    // ProxyGenerator.java
    private int proxyMethodCount = 3;

    private void addProxyMethod(Method m, Class<?> fromClass) {
        ...
        List<ProxyMethod> sigmethods = proxyMethods.computeIfAbsent(sig,
                (f) -> new ArrayList<>(3));
        ...
        sigmethods.add(new ProxyMethod(m, sig, m.getParameterTypes(), returnType,
                exceptionTypes, fromClass,
                "m" + proxyMethodCount++));
    }

很簡(jiǎn)單,就是proxyMethodCount開(kāi)始計(jì)數(shù)罷了。

生成接口方法的code

在generateClassFile方法中,生成接口方法code的時(shí)機(jī)發(fā)生在生成Constructor之后,代碼如下:

// ProxyGenerator.java

private byte[] generateClassFile() {
      ....
        // 憑空生成構(gòu)造器
        generateConstructor();

        for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
            for (ProxyMethod pm : sigmethods) {
                // add static field for the Method object
                visitField(Modifier.PRIVATE | Modifier.STATIC, pm.methodFieldName,
                        LJLR_METHOD, null, null);

                // 為所有上面添加的方法生成方法實(shí)現(xiàn)code
                pm.generateMethod(this, className);
            }
        }

    
        return toByteArray();
    }

不同于generateConstructor,generateMethod定義在ProxyMethod中:

       // ProxyMethod.java
       private void generateMethod(ClassWriter cw, String className) {
            ....
            MethodVisitor mv = cw.visitMethod(accessFlags,
                    method.getName(), desc, null,
                    typeNames(Arrays.asList(exceptionTypes)));

            ....
            // 開(kāi)始編寫(xiě)code部分
            mv.visitCode();
            ...
                
            // 準(zhǔn)備第一個(gè)參數(shù)this
            mv.visitVarInsn(ALOAD, 0);
            mv.visitFieldInsn(GETFIELD, JLR_PROXY, handlerFieldName,
                    LJLR_INVOCATION_HANDLER);
           // 準(zhǔn)備第二個(gè)參數(shù)
            mv.visitVarInsn(ALOAD, 0);
            mv.visitFieldInsn(GETSTATIC, dotToSlash(className), methodFieldName,
                    LJLR_METHOD);

            ....

            // invokeinterface調(diào)用方法
            mv.visitMethodInsn(INVOKEINTERFACE, JLR_INVOCATION_HANDLER,
                    "invoke",
                    "(Ljava/lang/Object;Ljava/lang/reflect/Method;" +
                            "[Ljava/lang/Object;)Ljava/lang/Object;", true);

            ...
            mv.visitEnd();
        }

該方法的結(jié)構(gòu)和generateConstructor一致,這里的MethodVisitor肯定也可以看做方法的descripter+accessFlags,重點(diǎn)解釋如下幾個(gè)棧指令調(diào)用:

mv.visitFieldInsn(GETFIELD, JLR_PROXY, handlerFieldName,LJLR_INVOCATION_HANDLER);
    JIR_PROXY -> private static final String JLR_PROXY = "java/lang/reflect/Proxy";
    handlerFieldName -> private static final String handlerFieldName = "h";
    LJLR_INVOCATION_HANDLER -> private static final String LJLR_INVOCATION_HANDLER = "Ljava/lang/reflect/InvocationHandler;"

mv.visitFieldInsn(GETSTATIC, dotToSlash(className), methodFieldName,LJLR_METHOD);
    methodFieldName -> "m0""m1"這種
    LJLR_METHOD -> private static final String LJLR_METHOD = "Ljava/lang/reflect/Method;"
        
// 接下來(lái)這個(gè)想必已經(jīng)不用解釋了
mv.visitMethodInsn(INVOKEINTERFACE, JLR_INVOCATION_HANDLER,
                    "invoke",
                    "(Ljava/lang/Object;Ljava/lang/reflect/Method;" +
                            "[Ljava/lang/Object;)Ljava/lang/Object;", true);

很顯然,在生成的代理類中,所有的方法實(shí)現(xiàn)都是如下形式:

h.invoke(this, m0, args);

即,全都委托給了代理類持有的InvocationHandler h的invoke方法來(lái)完成。

動(dòng)態(tài)代理總結(jié)

使用

動(dòng)態(tài)代理的使用最重要的就是InvocationHandler對(duì)象,該對(duì)象的invoke方法負(fù)責(zé)對(duì)代理類中所有方法進(jìn)行實(shí)現(xiàn),至關(guān)重要。

知識(shí)點(diǎn)

  1. 生成的代理類是Proxy這個(gè)類的子類,而后者的構(gòu)造器在代理類中被回調(diào),用于接收InvocationHandler對(duì)象;
  2. 代理類的所有方法都會(huì)在類中定義一個(gè)Method屬性,名字從m0開(kāi)始沿用;
  3. 代理類中所有方法實(shí)現(xiàn)都是如下模板:
class Proxy$0 {
    protected InvocationHandler h;
    private Method m3 = Class.forName("代理類的全限定名").getMethod("方法名", Class.forName("[java.lang.Objec或者接口全限定名"));
    public Object method1(Object[] args) {
        return h.invoke(this, m3, args);
    }
}

實(shí)例

最后我們來(lái)看看現(xiàn)象,以下實(shí)例來(lái)自文章https://mp.weixin.qq.com/s/gnj8x4bSQoNRMcRUWS6p5Q

public finalclass $Proxy0 extends Proxy implements Person { ★
    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 {   ④
        return (Boolean) super.h.invoke(this, m1, new Object[]{var1});
    }

    public final void rent() throws {   ③
        super.h.invoke(this, m3, (Object[]) null);
    }

    public final String toString() throws { ④
        return (String) super.h.invoke(this, m2, (Object[]) null);
    }

    public final int hashCode() throws {    ④
        return (Integer) super.h.invoke(this, m0, (Object[]) null);
    }

    static {    ①
        m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
        m3 = Class.forName("com.dujc.mybatis.proxy.Person").getMethod("rent");
        m2 = Class.forName("java.lang.Object").getMethod("toString");
        m0 = Class.forName("java.lang.Object").getMethod("hashCode");
    }
}

這就是將動(dòng)態(tài)代理生成的class文件反編譯出來(lái)的內(nèi)容,可以看到,和我們上文分析的一樣。

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

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

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