Analysis of dynamic proxy based on JDK implementation

well known

There are two common ways to implement dynamic proxy in Java:

  • Interface-based JDK dynamic proxy
  • Class-based CGLIB dynamic proxy

Today we will study the first one, which is the dynamic implementation of JDK

basic use

JDK's dynamic proxy, just upload the code directly

/**
 * 首先定义一个开发语言接口
 */
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("任何事"));
复制代码

How to implement dynamic proxy in Java?

The following code will omit the code that is less important and does not affect the logic of the result

First we start with his entrance

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

How do we generate proxy class files through the class file array of the interface?

This involves the getProxyClass0 method

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

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

Does the above example look confusing? Let's continue

The proxyClassCache.get method, in fact, this method is to obtain the production supplier of the proxy class class to obtain the proxy 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);
            }
        }
    }
}
复制代码

The next step is to actually get the proxy class.

get() method of Factory class

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

Finally, this is where the proxy class is actually generated.

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

in addition

The place where the class file is generated is the ProxyGenerator.generateProxyClass method. If you are interested, you can study it. The general logic is to inherit the Proxy and implement the interface to be proxyed, and generate the corresponding code. Use handle reflection to call the method of the proxy class and paste the code.

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 generates proxy class locally

configure first

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

image.png

Then set the system global variable before the method executes

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

image.png

The final generated proxy class is like this. Will it be easier to understand by referring to the class file to study its implementation?

image.png

Guess you like

Origin juejin.im/post/7104504015450275848