Análisis de proxy dinámico basado en implementación JDK

bien conocido

Hay dos formas comunes de implementar un proxy dinámico en Java:

  • Proxy dinámico JDK basado en interfaz
  • Proxy dinámico CGLIB basado en clases

Hoy estudiaremos el primero, que es la implementación dinámica de JDK

uso básico

Proxy dinámico de JDK, simplemente cargue el código directamente

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

¿Cómo implementar proxy dinámico en Java?

El siguiente código omitirá el código que es menos importante y no afecta la lógica del resultado

Primero comenzamos con su entrada.

Proxy.nuevaInstanciaProxy

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

¿Cómo generamos archivos de clase proxy a través de la matriz de archivos de clase de la interfaz?

Esto implica el método 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);
}
复制代码

¿El ejemplo anterior parece confuso? Continuemos

El método proxyClassCache.get, de hecho, este método es para obtener el proveedor de producción de la clase de clase de proxy para obtener la clase de proxy

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

El siguiente paso es obtener la clase de proxy.

método get() de la clase Factory

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

Finalmente, aquí es donde realmente se genera la clase proxy.

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

además

El lugar donde se genera el archivo de clase es el método ProxyGenerator.generateProxyClass, si te interesa lo puedes estudiar, la lógica general es heredar el Proxy e implementar la interfaz a ser proxy, y generar el código correspondiente, usa handle reflection para llamar al método de la clase proxy y pegar el código.

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

la idea genera una clase de proxy localmente

configurar primero

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

imagen.png

Luego establezca la variable global del sistema antes de que se ejecute el método

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

imagen.png

La clase proxy final generada es así. ¿Será más fácil de entender consultando el archivo de la clase para estudiar su implementación?

imagen.png

Supongo que te gusta

Origin juejin.im/post/7104504015450275848
Recomendado
Clasificación