• java动态代理-jdk动态代理原理解析


    场景

    动态代理是一个比较常见的场景,广泛用于方法增强、AOP(面向切面编程)、测试、监控等场景。市面上常用的动态代理主要是 jdk动态代理和cglib动态代理,javaassist 和 byteBuddy同样可以做动态代理。一般使用jdk动态代理代理方法 适合于代理接口,它不需要引入额外依赖,一般cglib适合于代理没有实现接口的类,性能较好。Javaassist就更加高级了,可以自由生成类字节码,可以自由改变类的行为,Byte Buddy主要是比较灵活,和 javaassist 差不多。我们之前使用Byte Buddy 做了自动生成接口的操作。

    jdk动态代理的原理

    demo

    
    interface MyInterface {
        void performAction();
    }
    
    class MyInterfaceImpl implements MyInterface {
        public void performAction() {
            System.out.println("Action performed");
        }
    }
    
    class MyInvocationHandler implements InvocationHandler {
        private Object target;
    
        public MyInvocationHandler(Object target) {
            this.target = target;
        }
    
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("Before method call");
            Object result = method.invoke(target, args);
            System.out.println("After method call");
            return result;
        }
    }
    
    public class JdkProxyDemo {
        public static void main(String[] args) {
            MyInterfaceImpl realObject = new MyInterfaceImpl();
            MyInterface proxyInstance = (MyInterface) Proxy.newProxyInstance(
                    MyInterfaceImpl.class.getClassLoader(),
                    new Class[]{MyInterface.class},
                    new MyInvocationHandler(realObject)
            );
            proxyInstance.performAction();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    jdk动态代理使用起来很简单,一个接口,n实现类 ,一个handler足够了,他的原理比较简单

        @CallerSensitive
        public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h) {
            Objects.requireNonNull(h);
    
            @SuppressWarnings("removal")
            final Class<?> caller = System.getSecurityManager() == null
                                        ? null
                                        : Reflection.getCallerClass();
    
            /*
             * Look up or generate the designated proxy class and its constructor.
             */
            Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
    
            return newProxyInstance(caller, cons, h);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    首先是检查handler和验证安全性,然后getProxyConstructor获取代理类的构造函数。

     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()
                );
            } 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()
                );
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    这个过程也很简单,首先是 单个接口
    当只有一个接口时,代码直接使用这个接口(Class intf = interfaces[0];)。
    在这种情况下,代理类只需要实现这一个接口,所以处理起来更直接、更简单。
    代理缓存(proxyCache)的子映射使用这个单一的接口作为键。首先验证完该类是否可以被代理之后,先去缓存拿取,拿不到则构建

        (ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
    
    • 1

    代理构造器

            ProxyBuilder(ClassLoader loader, List<Class<?>> interfaces) {
                if (!VM.isModuleSystemInited()) {
                    throw new InternalError("Proxy is not supported until "
                            + "module system is fully initialized");
                }
                if (interfaces.size() > 65535) {
                    throw new IllegalArgumentException("interface limit exceeded: "
                            + interfaces.size());
                }
    
                Set<Class<?>> refTypes = referencedTypes(loader, interfaces);
    
                // IAE if violates any restrictions specified in newProxyInstance
                validateProxyInterfaces(loader, interfaces, refTypes);
    
                this.interfaces = interfaces;
                this.module = mapToModule(loader, interfaces, refTypes);
                assert getLoader(module) == loader;
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    首先检查模块是否已经完全初始化,这是j9引入的新特性,模块的概念,模块系统的初始化是启动过程的一个关键部分。它负责设置模块路径,确定哪些模块是可访问的,以及它们如何相互作用。
    如果模块系统没有完全初始化,许多基于模块的操作,比如动态代理的创建,都不能正常进行。这是因为代理类可能需要跨越不同模块的边界,访问它们的类或接口。

    其次检查接口数量是否大于65535,是否超出限制。

    再来就是收集引用类型

          /*
             * Returns all types referenced by all public non-static method signatures of
             * the proxy interfaces
             */
            private static Set<Class<?>> referencedTypes(ClassLoader loader,
                                                         List<Class<?>> interfaces) {
                var types = new HashSet<Class<?>>();
                for (var intf : interfaces) {
                    for (Method m : intf.getMethods()) {
                        if (!Modifier.isStatic(m.getModifiers())) {
                            addElementType(types, m.getReturnType());
                            addElementTypes(types, m.getSharedParameterTypes());
                            addElementTypes(types, m.getSharedExceptionTypes());
                        }
                    }
                }
                return types;
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    其实就是收集接口中有哪些方法,方法中有哪些参数,返回值是什么,异常信息是什么这些云云。这个方法的目的是为动态代理类的创建准备必要的类型信息

    再来就是验证代理接口:

    validateProxyInterfaces(loader, interfaces, refTypes);
    
    • 1

    非常简单

        private static void validateProxyInterfaces(ClassLoader loader,
                                                        List<Class<?>> interfaces,
                                                        Set<Class<?>> refTypes)
            {
                Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.size());
                for (Class<?> intf : interfaces) {
                    /*
                     * Verify that the Class object actually represents an
                     * interface.
                     */
                    if (!intf.isInterface()) {
                        throw new IllegalArgumentException(intf.getName() + " is not an interface");
                    }
    
                    if (intf.isHidden()) {
                        throw new IllegalArgumentException(intf.getName() + " is a hidden interface");
                    }
    
                    if (intf.isSealed()) {
                        throw new IllegalArgumentException(intf.getName() + " is a sealed interface");
                    }
    
                    /*
                     * Verify that the class loader resolves the name of this
                     * interface to the same Class object.
                     */
                    ensureVisible(loader, intf);
    
                    /*
                     * Verify that this interface is not a duplicate.
                     */
                    if (interfaceSet.put(intf, Boolean.TRUE) != null) {
                        throw new IllegalArgumentException("repeated interface: " + intf.getName());
                    }
                }
    
                for (Class<?> type : refTypes) {
                    ensureVisible(loader, type);
                }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    验证是不是一个接口,是不是隐藏和密封(15新特性)的接口,在15,引入了隐藏和密封的概念,随后验证接口和之前获取的类型能不能被获取加载。

    再来就是完善构建器

                this.interfaces = interfaces;
                this.module = mapToModule(loader, interfaces, refTypes);
                assert getLoader(module) == loader;
    
    • 1
    • 2
    • 3

    再来就是构建 构造方法了

       Constructor<?> build() {
                Class<?> proxyClass = defineProxyClass(module, interfaces);
                assert !module.isNamed() || module.isOpen(proxyClass.getPackageName(), Proxy.class.getModule());
    
                final Constructor<?> cons;
                try {
                    cons = proxyClass.getConstructor(constructorParams);
                } catch (NoSuchMethodException e) {
                    throw new InternalError(e.toString(), e);
                }
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
                return cons;
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
      private static Class<?> defineProxyClass(Module m, List<Class<?>> interfaces) {
                String proxyPkg = null;     // package to define proxy class in
                int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
                boolean nonExported = false;
    
                /*
                 * Record the package of a non-public proxy interface so that the
                 * proxy class will be defined in the same package.  Verify that
                 * all non-public proxy interfaces are in the same package.
                 */
                for (Class<?> intf : interfaces) {
                    int flags = intf.getModifiers();
                    if (!Modifier.isPublic(flags)) {
                        accessFlags = Modifier.FINAL;  // non-public, final
                        String pkg = intf.getPackageName();
                        if (proxyPkg == null) {
                            proxyPkg = pkg;
                        } else if (!pkg.equals(proxyPkg)) {
                            throw new IllegalArgumentException(
                                    "non-public interfaces from different packages");
                        }
                    } else {
                        if (!intf.getModule().isExported(intf.getPackageName())) {
                            // module-private types
                            nonExported = true;
                        }
                    }
                }
    
                if (proxyPkg == null) {
                    // all proxy interfaces are public and exported
                    if (!m.isNamed())
                        throw new InternalError("ununamed module: " + m);
                    proxyPkg = nonExported ? PROXY_PACKAGE_PREFIX + "." + m.getName()
                                           : m.getName();
                } else if (proxyPkg.isEmpty() && m.isNamed()) {
                    throw new IllegalArgumentException(
                            "Unnamed package cannot be added to " + m);
                }
    
                if (m.isNamed()) {
                    if (!m.getDescriptor().packages().contains(proxyPkg)) {
                        throw new InternalError(proxyPkg + " not exist in " + m.getName());
                    }
                }
    
                /*
                 * Choose a name for the proxy class to generate.
                 */
                long num = nextUniqueNumber.getAndIncrement();
                String proxyName = proxyPkg.isEmpty()
                                        ? proxyClassNamePrefix + num
                                        : proxyPkg + "." + proxyClassNamePrefix + num;
    
                ClassLoader loader = getLoader(m);
                trace(proxyName, m, loader, interfaces);
    
                /*
                 * Generate the specified proxy class.
                 */
                byte[] proxyClassFile = 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;
                } catch (ClassFormatError e) {
                    /*
                     * A ClassFormatError here means that (barring bugs in the
                     * proxy class generation code) there was some other
                     * invalid aspect of the arguments supplied to the proxy
                     * class creation (such as virtual machine limitations
                     * exceeded).
                     */
                    throw new IllegalArgumentException(e.toString());
                }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77

    这个方法意思就是创建一个代理类,方法比较长,但是无难度。

     String proxyPkg = null;     // package to define proxy class in
                int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
                boolean nonExported = false;
    
    • 1
    • 2
    • 3

    首先定义包名访问符和接口是否有接口未被模块导出等变量 一个初始化的操作啊。

       for (Class<?> intf : interfaces) {
                    int flags = intf.getModifiers();
                    if (!Modifier.isPublic(flags)) {
                        accessFlags = Modifier.FINAL;  // non-public, final
                        String pkg = intf.getPackageName();
                        if (proxyPkg == null) {
                            proxyPkg = pkg;
                        } else if (!pkg.equals(proxyPkg)) {
                            throw new IllegalArgumentException(
                                    "non-public interfaces from different packages");
                        }
                    } else {
                        if (!intf.getModule().isExported(intf.getPackageName())) {
                            // module-private types
                            nonExported = true;
                        }
                    }
                }
                 if (proxyPkg == null) {
                    // all proxy interfaces are public and exported
                    if (!m.isNamed())
                        throw new InternalError("ununamed module: " + m);
                    proxyPkg = nonExported ? PROXY_PACKAGE_PREFIX + "." + m.getName()
                                           : m.getName();
                } else if (proxyPkg.isEmpty() && m.isNamed()) {
                    throw new IllegalArgumentException(
                            "Unnamed package cannot be added to " + m);
                }
    
                if (m.isNamed()) {
                    if (!m.getDescriptor().packages().contains(proxyPkg)) {
                        throw new InternalError(proxyPkg + " not exist in " + m.getName());
                    }
                }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    他把接口给遍历了啊,注意这里是有单一包名限制的。
    遍历传入的接口列表interfaces。对于每个接口intf:
    不是公共的,设置为FINAL,获取包名,如果接口的包名与proxyPkg不同,则抛出异常。
    else:如果接口是公共的:
    if (!intf.getModule().isExported(intf.getPackageName())):如果接口所在的模块没有导出该接口的包,则将nonExported设置为true。
    上面的代码主打一个验证,就是杂七杂八的验证。

    给代理类起名字

     /*
                 * Choose a name for the proxy class to generate.
                 */
                long num = nextUniqueNumber.getAndIncrement();
                String proxyName = proxyPkg.isEmpty()
                                        ? proxyClassNamePrefix + num
                                        : proxyPkg + "." + proxyClassNamePrefix + num;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    获取类加载器并记录跟踪信息,随后生成代理类的字节码并定义类,然后定义代理

    
                ClassLoader loader = getLoader(m);
                trace(proxyName, m, loader, interfaces);
    
                /*
                 * Generate the specified proxy class.
                 */
                byte[] proxyClassFile = 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;
                } catch (ClassFormatError e) {
                    /*
                     * A ClassFormatError here means that (barring bugs in the
                     * proxy class generation code) there was some other
                     * invalid aspect of the arguments supplied to the proxy
                     * class creation (such as virtual machine limitations
                     * exceeded).
                     */
                    throw new IllegalArgumentException(e.toString());
                }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    重要代码

    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(loader, proxyName, interfaces, accessFlags);:使用ProxyGenerator生成代理类的字节码。
    
    • 1
    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();
    
            if (saveGeneratedFiles) {
                java.security.AccessController.doPrivileged(
                        new java.security.PrivilegedAction<Void>() {
                            public Void run() {
                                try {
                                    int i = name.lastIndexOf('.');
                                    Path path;
                                    if (i > 0) {
                                        Path dir = Path.of(dotToSlash(name.substring(0, i)));
                                        Files.createDirectories(dir);
                                        path = dir.resolve(name.substring(i + 1) + ".class");
                                    } else {
                                        path = Path.of(name + ".class");
                                    }
                                    Files.write(path, classFile);
                                    return null;
                                } catch (IOException e) {
                                    throw new InternalError(
                                            "I/O exception saving generated file: " + e);
                                }
                            }
                        });
            }
    
            return classFile;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    ProxyGenerator gen = new ProxyGenerator(loader, name, interfaces, accessFlags); 创建代理生成器,生成字节码:

    final byte[] classFile = gen.generateClassFile();:通过调用ProxyGenerator对象的generateClassFile方法,生成代理类的字节码,并将其存储在字节数组classFile中。
    
    • 1

    给字节码中注入方法,先注入基本方法 什么 hashCode 什么 equals 什么 toString ,然后再把接口方法注入进去,然后检查返回参数,随后生成构造方法,其实我们的关注点应该在generateMethod 方法上,这才是灵魂。

      private byte[] generateClassFile() {
            visit(V14, accessFlags, dotToSlash(className), null,
                    JLR_PROXY, typeNames(interfaces));
    
            /*
             * Add proxy methods for the hashCode, equals,
             * and toString methods of java.lang.Object.  This is done before
             * the methods from the proxy interfaces so that the methods from
             * java.lang.Object take precedence over duplicate methods in the
             * proxy interfaces.
             */
            addProxyMethod(hashCodeMethod);
            addProxyMethod(equalsMethod);
            addProxyMethod(toStringMethod);
    
            /*
             * Accumulate all of the methods from the proxy interfaces.
             */
            for (Class<?> intf : interfaces) {
                for (Method m : intf.getMethods()) {
                    if (!Modifier.isStatic(m.getModifiers())) {
                        addProxyMethod(m, intf);
                    }
                }
            }
    
            /*
             * For each set of proxy methods with the same signature,
             * verify that the methods' return types are compatible.
             */
            for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
                checkReturnTypes(sigmethods);
            }
    
            generateConstructor();
    
            for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
                for (ProxyMethod pm : sigmethods) {
                    // add static field for the Method object
                    visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, pm.methodFieldName,
                            LJLR_METHOD, null, null);
    
                    // Generate code for proxy method
                    pm.generateMethod(this, className);
                }
            }
    
            generateStaticInitializer();
            generateLookupAccessor();
            return toByteArray();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
      private void addProxyMethod(ProxyMethod pm) {
            String sig = pm.shortSignature;
            List<ProxyMethod> sigmethods = proxyMethods.computeIfAbsent(sig,
                    (f) -> new ArrayList<>(3));
            sigmethods.add(pm);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    灵魂方法: 这个方法它实现了将方法调用转发到InvocationHandler的逻辑。

          private void generateMethod(ClassWriter cw, String className) {
                MethodType mt = MethodType.methodType(returnType, parameterTypes);
                String desc = mt.toMethodDescriptorString();
                int accessFlags = ACC_PUBLIC | ACC_FINAL;
                if (method.isVarArgs()) accessFlags |= ACC_VARARGS;
    
                MethodVisitor mv = cw.visitMethod(accessFlags,
                        method.getName(), desc, null,
                        typeNames(Arrays.asList(exceptionTypes)));
    
                int[] parameterSlot = new int[parameterTypes.length];
                int nextSlot = 1;
                for (int i = 0; i < parameterSlot.length; i++) {
                    parameterSlot[i] = nextSlot;
                    nextSlot += getWordsPerType(parameterTypes[i]);
                }
    
                mv.visitCode();
                Label L_startBlock = new Label();
                Label L_endBlock = new Label();
                Label L_RuntimeHandler = new Label();
                Label L_ThrowableHandler = new Label();
    
                List<Class<?>> catchList = computeUniqueCatchList(exceptionTypes);
                if (catchList.size() > 0) {
                    for (Class<?> ex : catchList) {
                        mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_RuntimeHandler,
                                dotToSlash(ex.getName()));
                    }
    
                    mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_ThrowableHandler,
                            JL_THROWABLE);
                }
                mv.visitLabel(L_startBlock);
    
                mv.visitVarInsn(ALOAD, 0);
                mv.visitFieldInsn(GETFIELD, JLR_PROXY, handlerFieldName,
                        LJLR_INVOCATION_HANDLER);
                mv.visitVarInsn(ALOAD, 0);
                mv.visitFieldInsn(GETSTATIC, dotToSlash(className), methodFieldName,
                        LJLR_METHOD);
    
                if (parameterTypes.length > 0) {
                    // Create an array and fill with the parameters converting primitives to wrappers
                    emitIconstInsn(mv, parameterTypes.length);
                    mv.visitTypeInsn(Opcodes.ANEWARRAY, JL_OBJECT);
                    for (int i = 0; i < parameterTypes.length; i++) {
                        mv.visitInsn(DUP);
                        emitIconstInsn(mv, i);
                        codeWrapArgument(mv, parameterTypes[i], parameterSlot[i]);
                        mv.visitInsn(Opcodes.AASTORE);
                    }
                } else {
                    mv.visitInsn(Opcodes.ACONST_NULL);
                }
    
                mv.visitMethodInsn(INVOKEINTERFACE, JLR_INVOCATION_HANDLER,
                        "invoke",
                        "(Ljava/lang/Object;Ljava/lang/reflect/Method;" +
                                "[Ljava/lang/Object;)Ljava/lang/Object;", true);
    
                if (returnType == void.class) {
                    mv.visitInsn(POP);
                    mv.visitInsn(RETURN);
                } else {
                    codeUnwrapReturnValue(mv, returnType);
                }
    
                mv.visitLabel(L_endBlock);
    
                // Generate exception handler
                mv.visitLabel(L_RuntimeHandler);
                mv.visitInsn(ATHROW);   // just rethrow the exception
    
                mv.visitLabel(L_ThrowableHandler);
                mv.visitVarInsn(ASTORE, 1);
                mv.visitTypeInsn(Opcodes.NEW, JLR_UNDECLARED_THROWABLE_EX);
                mv.visitInsn(DUP);
                mv.visitVarInsn(ALOAD, 1);
                mv.visitMethodInsn(INVOKESPECIAL, JLR_UNDECLARED_THROWABLE_EX,
                        "", "(Ljava/lang/Throwable;)V", false);
                mv.visitInsn(ATHROW);
                // Maxs computed by ClassWriter.COMPUTE_FRAMES, these arguments ignored
                mv.visitMaxs(-1, -1);
                mv.visitEnd();
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86

    核心是 生成代理方法的主体:
    加载this和InvocationHandler引用,以及被调用的方法对象。
    创建参数对象数组,并将方法参数转换为对象,存储在数组中。
    使用InvocationHandler的invoke方法来实现方法的动态代理调用。

    加载this和InvocationHandler引用,以及被调用的方法对象:
    mv.visitVarInsn(ALOAD, 0);:加载this引用到栈顶。在Java字节码中,ALOAD用于加载一个对象引用到栈顶,而这里的0表示加载位于局部变量表中索引为0的对象,即this引用。

    mv.visitFieldInsn(GETFIELD, JLR_PROXY, handlerFieldName, LJLR_INVOCATION_HANDLER);:获取当前代理实例的InvocationHandler字段,将其压入操作数栈。这里GETFIELD是用于访问对象的字段。

    mv.visitVarInsn(ALOAD, 0);:再次加载this引用到栈顶。

    mv.visitFieldInsn(GETSTATIC, dotToSlash(className), methodFieldName, LJLR_METHOD);:获取静态字段,即当前正在执行的Method对象的引用。

    我们回来

         Constructor<?> build() {
                Class<?> proxyClass = defineProxyClass(module, interfaces);
                assert !module.isNamed() || module.isOpen(proxyClass.getPackageName(), Proxy.class.getModule());
    
                final Constructor<?> cons;
                try {
                    cons = proxyClass.getConstructor(constructorParams);
                } catch (NoSuchMethodException e) {
                    throw new InternalError(e.toString(), e);
                }
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
                return cons;
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    类都生成了 拿到构造简直是小菜一碟了,后面自然是绕过安全检查,设置构造可见。

    
        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});
            } catch (IllegalAccessException | InstantiationException e) {
                throw new InternalError(e.toString(), e);
            } catch (InvocationTargetException e) {
                Throwable t = e.getCause();
                if (t instanceof RuntimeException) {
                    throw (RuntimeException) t;
                } else {
                    throw new InternalError(t.toString(), t);
                }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    然后就示例化了,最复杂的是生成代理类那一块。

    生成后代理类伪代码应该是

    import java.lang.reflect.*;
    
    public class DynamicProxyClass implements HelloInterface {
        private InvocationHandler handler;
    
        public DynamicProxyClass(InvocationHandler handler) {
            this.handler = handler;
        }
    
        @Override
        public void performAction() {
            try {
                Method method = HelloInterface.class.getMethod("performAction");
                this.handler.invoke(this, method, null);
            } catch (Throwable throwable) {
                // 处理异常
            }
        }
        @Override
        public String toString() {
            try {
                Method method = Object.class.getMethod("toString");
                return (String) handler.invoke(this, method, null);
            } catch (Throwable throwable) {
                // 处理异常
                return null;
            }
        }
    
        @Override
        public boolean equals(Object obj) {
            try {
                Method method = Object.class.getMethod("equals", Object.class);
                return (Boolean) handler.invoke(this, method, new Object[]{obj});
            } catch (Throwable throwable) {
                // 处理异常
                return false;
            }
        }
    
        @Override
        public int hashCode() {
            try {
                Method method = Object.class.getMethod("hashCode");
                return (Integer) handler.invoke(this, method, null);
            } catch (Throwable throwable) {
                // 处理异常
                return 0;
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    差不多就这样。

    总结

    差不多就是 先 接口定义->实现InvocationHandler -> 安全性检查 -> 生成代理类字节码 -> 实例化代理类 不复杂。 比起 javaaasit和bybtBuddy 还是很简单,弟中弟了 属于是。

  • 相关阅读:
    AI人工智能电话机器人应用市场分析
    APUE 第4章: cp命令实现,同时去除文件中的空洞
    Python实现类属性的延迟加载装饰器
    Seata流程源码梳理下篇-TC
    BetterDisplay Pro v1.4.15(显示器管理管理软件)
    利用 Kubernetes 降本增效?EasyMR 基于 Kubernetes 部署的探索实践
    SpringBoot的 8 个优点
    线性表的概念
    completefuture造成的rpc重试事故
    关于在Linux服务上安装nvm,踩坑步骤说明
  • 原文地址:https://blog.csdn.net/weixin_45487988/article/details/134438679