分类
Java

JDK 动态代理的实现

我们知道,JDK 动态代理可以对接口进行代理,把接口的所有方法代理到 InvocationHandler 的 invoke 方法上。
示例如下:

package com.youthlin.example.proxy.service;

public interface IUserService {
    String getName();
}
package com.youthlin.example.proxy;

import com.youthlin.example.proxy.service.IUserService;
import sun.misc.ProxyGenerator;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 创建: youthlin.chen
 * 时间: 2017-11-28 11:49
 */
public class Main {

    public static void main(String[] args) throws IOException {
        IUserService userService = (IUserService) Proxy.newProxyInstance(
                IUserService.class.getClassLoader(),
                new Class[]{IUserService.class},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        return "YouthLin";
                    }
                });

        System.out.println("不同包接口 " + userService.getName());

        //writeProxyClass(userService.getClass());
    }

}

这样我们就可以对一个 没有实现 的接口进行调用了,实际的调用代理给了 IncovationHandler 实例的 invoke 方法。那么这个是怎么实现的呢?

我们可以使用 sun.misc.ProxyGenerator#generateProxyClass(java.lang.String, java.lang.Class<?>[]) 这个方法获取到生成的代理类的字节码数组,把字节码输出为一个 .class 文件,在 IDEA 中就可以看到源码。

    private static void writeProxyClass(Class<?> proxyClass) throws IOException {
        String proxyClassName = proxyClass.getName();
        //System.writeProxyClass.println(proxyClassName);
        proxyClassName = proxyClassName.substring(proxyClassName.lastIndexOf(".") + 1);
        //System.writeProxyClass.println(proxyClassName);
        byte[] proxyClassBytes = ProxyGenerator.generateProxyClass(proxyClassName, new Class[]{IHelloService.class});
        String path = Main.class.getResource("/").getPath() + "/generate/";
        File file = new File(path, proxyClass.getName().replaceAll("\\.", "/") + ".class");
        if (!file.getParentFile().exists()) {
            //noinspection ResultOfMethodCallIgnored
            file.getParentFile().mkdirs();
        }
        System.out.println(proxyClass.getName() + " " + file.getAbsolutePath());
        FileOutputStream out = new FileOutputStream(file);
        out.write(proxyClassBytes);
        out.flush();
        out.close();
    }

然后在 main 方法里调用 writeProxyClass(userService.getClass()); 就可以了。用 IDEA 打开输出文件,可以看到:

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

import com.youthlin.example.proxy.Main.IHelloService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy1 extends Proxy implements IHelloService {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

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

    public final boolean equals(Object var1) throws  {
        try {
            return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String sayHello(String var1) throws  {
        try {
            return (String)super.h.invoke(this, m3, 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)).intValue();
        } 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.youthlin.example.proxy.Main$IHelloService").getMethod("sayHello", Class.forName("java.lang.String"));
            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());
        }
    }
}

也就是说,生成动态代理对象时,实际上是 JDK 动态编译了一个类,继承了 Proxy 类,实现了传入的接口类。然后把接口的方法(及 Object 的三个实例方法 toString, hashCode, equals)都代理给了父类 Proxy 的 InvocationHandler 实例。所以我们可以在自定义的 InvocationHandler 的 invoke 方法中进行处理。

Proxy

java.lang.reflect.Proxy#newProxyInstance

这个方法用于生成代理对象,里面比较重要的就是 Class<?> cl = getProxyClass0(loader, intfs); 这一句,拿到代理类的 Class 实例,之后 return cons.newInstance(new Object[]{h}); 即可生成代理对象。

java.lang.reflect.Proxy#getProxyClass0

这个方法实现很简单: return proxyClassCache.get(loader, interfaces); 从一个 proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory()); 缓存中获取类实例。其实真正的获取在这个地方:

java.lang.reflect.Proxy.ProxyClassFactory#apply

首先一些权限检查之类的,略过,拿到代理对象的类名,最终也落在 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); 这个调用上。最后通过本地方法将 byte[] 转化为 Class 对象就完成了。

ProxyGenerator

sun.misc.ProxyGenerator#generateProxyClass(java.lang.String, java.lang.Class<?>[], int)
sun.misc.ProxyGenerator#generateClassFile

这里就是动态生成字节码的地方了。
其实是根据 JVM 规范,把类信息写入了字节数组

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

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

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

                while(var15.hasNext()) {
                    ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
                    var20.write(var14);
                }

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

                while(var15.hasNext()) {
                    ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
                    var21.write(var14);
                }

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

对应下面的 Class 规范:
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html

ClassFile {
    u4             magic;
    u2             minor_version;
    u2             major_version;
    u2             constant_pool_count;
    cp_info        constant_pool[constant_pool_count-1];
    u2             access_flags;
    u2             this_class;
    u2             super_class;
    u2             interfaces_count;
    u2             interfaces[interfaces_count];
    u2             fields_count;
    field_info     fields[fields_count];
    u2             methods_count;
    method_info    methods[methods_count];
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}
JDKProxy
JDKProxy

本文代码可以在这里找到:
https://github.com/YouthLin/examples/tree/master/example-jdk-proxy


“JDK 动态代理的实现”上的1条回复

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

[/鼓掌] [/难过] [/调皮] [/白眼] [/疑问] [/流泪] [/流汗] [/撇嘴] [/抠鼻] [/惊讶] [/微笑] [/得意] [/大兵] [/坏笑] [/呲牙] [/吓到] [/可爱] [/发怒] [/发呆] [/偷笑] [/亲亲]