浅析基于JDK实现的动态代理

众所周知

Java中实现动态代理常用的方式有两个:

  • 基于接口的JDK动态代理
  • 基于类的CGLIB动态代理

今天我们先来研究下第一种,就是JDK的动态实现

基本使用

JDK的动态代理,就直接上代码

/**
 * 首先定义一个开发语言接口
 */
public interface DevelopLanguage {

    String canDo(String what);
}

/**
 * 接口的实现类
 */
public class Java implements DevelopLanguage {

    private String name;
    
    public Java() {
    }

    public Java(String name){
        this.name = name;
    }

    @Override
    public String canDo(String what) {
        return name + "可以做" + what;
    }
}

// 创建Java实例
DevelopLanguage language = new Java("Java JDK proxy");

// 调用JDK动态代理API生成代理对象
DevelopLanguage languageJDKProxy = (DevelopLanguage) Proxy.newProxyInstance(DevelopLanguage.class.getClassLoader(), new Class[]{DevelopLanguage.class}, new InvocationHandler() {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(language,args);
    }
});

// 代理对象调用接口
System.out.println(languageJDKProxy.canDo("任何事"));
复制代码

Java内部究竟是怎么实现动态代理的呢

下面代码都会省略不太重要不影响结果逻辑的代码

首先我们从他的入口入手

Proxy.newProxyInstance

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

    // 浅克隆一份接口的class文件数组,数组是另一块内存空间;但数组里面的class还是同一个class,都是指向同一块内存地址
    final Class<?>[] intfs = interfaces.clone();

    // 查找或生成指定的代理类class文件,下面在说这个方法
    Class<?> cl = getProxyClass0(loader, intfs);
    
    // 获取代理class的有参构造方法(参数就是InvocationHandler接口)
    final Constructor<?> cons = cl.getConstructor(constructorParams);

    final InvocationHandler ih = h;
    // 此处判断构造方法是否是公开权限,如果不是则设置访问权限
    if (!Modifier.isPublic(cl.getModifiers())) {
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
                cons.setAccessible(true);
                return null;
            }
        });
    }
    
    // 反射实例化代理对象
    return cons.newInstance(new Object[]{h});
}
复制代码

我们怎么通过接口的class文件数组生成代理类文件的呢

这就涉及到getProxyClass0方法了

private static Class<?> getProxyClass0(ClassLoader loader,
                                       Class<?>... interfaces) {
    // 限定代理的接口数量
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }

    // 通过代码注释了解到:如果缓存中有这个类加载器的接口代理实现,则获取代理实现的副本,
    // 否则通过ProxyClassFactory创建
    // 缓存是通过弱引用来缓存
    return proxyClassCache.get(loader, interfaces);
}
复制代码

上面的例子是不是看的一脸懵逼,我们继续

proxyClassCache.get方法,其实这个方法就是获取代理类class的生产供应商以获取代理类

public V get(K key, P parameter) {

    // 创建一个以classloader为引用的缓存键
    Object cacheKey = CacheKey.valueOf(key, refQueue);

    // map中有二级缓存 
    // 第一级缓存:通过上面创建的缓存键获取对应的通过该类加载器加载的所有代理类,也就是第二级缓存的Map 
    // 第二级缓存:以接口创建的虚引用为键,对应接口的代理类的供应商为值 
    // 此处就是通过classloader去获取到第二级缓存
    ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
    
    // 没有缓存,构建缓存
    if (valuesMap == null) {
        ConcurrentMap<Object, Supplier<V>> oldValuesMap
            = map.putIfAbsent(cacheKey,
                              valuesMap = new ConcurrentHashMap<>());
        if (oldValuesMap != null) {
            valuesMap = oldValuesMap;
        }
    }

    // 创建第二级缓存的key
    Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
    // 获取到这个类加载器的接口代理类的生产商
    Supplier<V> supplier = valuesMap.get(subKey);
    Factory factory = null;

    // 死循环去生成代理类,没有工厂就取建工厂,没有生产商再去建生产商
    // Factory其实就是Supplier的实现
    while (true) {
        // 如果生产商不为空,通过生产商生成对应的代理类class
        if (supplier != null) {
            // 生产商生产代理类
            V value = supplier.get();
            if (value != null) {
                return value;
            }
        }
        
        // 懒加载一个工厂
        if (factory == null) {
            factory = new Factory(key, parameter, subKey, valuesMap);
        }

        // 如果供应商不存在
        if (supplier == null) {
            // 那么则将上面创建的供应商存入缓存中
            // 然后重新开始循环
            supplier = valuesMap.putIfAbsent(subKey, factory);
            if (supplier == null) {
                // 将创建的工厂赋值供应商
                supplier = factory;
            }
        } 
        
        else {
            // 如果存在供应商,但是没有获取到代理类 
            // 那么则将新创建的供应商替换旧的供应商
            if (valuesMap.replace(subKey, supplier, factory)) {
                // 替换成功,将创建的工厂赋值给供应商,继续循环获取代理类class
                supplier = factory;
            } else {
                // 替换不成功,尝试用旧的供应商获取代理类class
                supplier = valuesMap.get(subKey);
            }
        }
    }
}
复制代码

接下来就到了真正获取代理类的地方了

Factory类的get()方法

public synchronized V get() { // serialize access
        // 重新检查缓存中的供应商是否是当前对象
        Supplier<V> supplier = valuesMap.get(subKey);
        if (supplier != this) {
            return null;
        }

        V value = null;
        try {
            // 真正生成代理类class的地方
            // valueFactory是ProxyClassFactory类的实例对象
            value = Objects.requireNonNull(valueFactory.apply(key, parameter));
        } finally {
            if (value == null) { 
                // 如果供应商没有生成代理类class,将供应商移除缓存
                valuesMap.remove(subKey, this);
            }
        }
        // the only path to reach here is with non-null value
        assert value != null;

        // 为代理类创建一个虚引用
        CacheValue<V> cacheValue = new CacheValue<>(value);

        reverseMap.put(cacheValue, Boolean.TRUE);

        // 尝试将新创建的代理类的缓存供应商替换旧的供应商 
        // 也就是上一个方法创建Factory类的对象 
        // 该方法执行必须成功
        if (!valuesMap.replace(subKey, this, cacheValue)) {
            throw new AssertionError("Should not reach here");
        }

        return value;
    }
}
复制代码

最后就是代理类真正生成的地方了

ProxyClassFactory.apply

public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

    Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
    for (Class<?> intf : interfaces) {
        // 此处校验class,省略
        ...
    }

    // 代理类的包名
    String proxyPkg = null;     // package to define proxy class in
    int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

    // 验证所有非公共代理接口都在同一个包中
    for (Class<?> intf : interfaces) {
        int flags = intf.getModifiers();
        if (!Modifier.isPublic(flags)) {
            accessFlags = Modifier.FINAL;
            String name = intf.getName();
            int n = name.lastIndexOf('.');
            String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
            if (proxyPkg == null) {
                proxyPkg = pkg;
            } else if (!pkg.equals(proxyPkg)) {
                throw new IllegalArgumentException(
                    "non-public interfaces from different packages");
            }
        }
    }

    if (proxyPkg == null) {
        proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
    }

    // //代理类的全限定类名
    long num = nextUniqueNumber.getAndIncrement();
    String proxyName = proxyPkg + proxyClassNamePrefix + num;

    // 生成class文件流
    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
        proxyName, interfaces, accessFlags);
    try {
        // 通过类文件创建Class对象,这是一个本地方法(C/C++实现),无法查看
        return defineClass0(loader, proxyName,
                            proxyClassFile, 0, proxyClassFile.length);
    } catch (ClassFormatError e) {
        throw new IllegalArgumentException(e.toString());
    }
}
复制代码

另外

生成class文件的地方就是ProxyGenerator.generateProxyClass方法,有兴趣可以研究下,大概的逻辑就是继承Proxy并实现要代理的接口,并生成相应的代码,利用handle反射调用代理的类的方法,贴上代码

public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
    ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
    final byte[] var4 = var3.generateClassFile();
    if (saveGeneratedFiles) {
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
                try {
                    int var1 = var0.lastIndexOf(46);
                    Path var2;
                    if (var1 > 0) {
                        Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
                        Files.createDirectories(var3);
                        var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
                    } else {
                        var2 = Paths.get(var0 + ".class");
                    }

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

    return var4;
}

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 var4;
    for(var3 = 0; var3 < var2; ++var3) {
        var4 = var1[var3];
        Method[] var5 = var4.getMethods();
        int var6 = var5.length;

        for(int var7 = 0; var7 < var6; ++var7) {
            Method var8 = var5[var7];
            this.addProxyMethod(var8, var4);
        }
    }

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

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

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

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

            while(var15.hasNext()) {
                ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
                this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
                this.methods.add(var16.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) {
            var4 = var1[var3];
            this.cp.getClass(dotToSlash(var4.getName()));
        }

        this.cp.setReadOnly();
        ByteArrayOutputStream var13 = new ByteArrayOutputStream();
        DataOutputStream var14 = new DataOutputStream(var13);

        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);
        }
    }
}
复制代码

idea本地生成代理class

首先配置

VM options:-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
复制代码

image.png

然后在方法执行之前设置系统全局变量

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
复制代码

image.png

最后生成的代理class就是这样了,参照class文件去研究它的实现会不会跟容易理解呢

image.png

猜你喜欢

转载自juejin.im/post/7104504015450275848