Java的字节码操作框架ASM详解

文章目录

    • 一、ASM介绍
      • 1. 简介
      • 2. 基本概念
      • 3. Java ASM的应用场景
      • 二、ASM基本使用
        • 1. 基本使用
        • 2. Java ASM主要组件
        • 3. Java ASM使用组件准备
        • 4. 修改字节码
        • 三、Java ASM高级技巧
          • 1. 自定义类加载器
          • 2. 动态代理

            一、ASM介绍

            1. 简介

            ASM是一个用于在Java字节码级别进行操作的框架,它运行你在不破坏原有逻辑的情况下修改、生成和转换字节码。ASM提供类一种灵活高效的方式,用于在编译器运行时动态生成字节码,这对于许多Java框架和工具来说是非常有用的。

            2. 基本概念

            字节码:字节码是Java源代码编译后的中间表达形式,它由字节码指令组成,这些指令被Java虚拟机执行。

            访问者模式:ASM使用访问者模式,它通过访问者来访问和操作字节码。在ASM中,你可以实现自定义的访问者来处理不同类型的字节码元素。

            类适配器和方法适配器:ASM提供了类适配器和方法适配器,用于修改类和方法的字节码。类适配器可以用于全局类操作,而方法适配器则可以用于单个方法的操作。

            3. Java ASM的应用场景

            1. 代码分析和优化:通过ASM,我们可以对字节码进行深入分析,以找出潜在的性能瓶颈或者执行特定的优化
            2. 动态代理和AOP(面向切面编程):ASM可以用于创建动态代理或实现AOP框架,从而实现运行时的行为修改
            3. 自定义类加载器:通过ASM,我们可以实现自定义类加载器,以支持独特的类加载策略和实现热加载等功能
            4. 安全审计:ASM可以用于分析潜在的安全风险,例如检测恶意代码或验证第三方库的安全性

            二、ASM基本使用

            1. 基本使用

            添加ASM依赖

             org.ow2.asm asm 9.2 

            生成一个类

            public class HelloWorldGenerator { public static byte[] generateHelloWorldClass() { ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
                    // Define the class
                    cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "HelloWorld", null, "java/lang/Object", null);
                    // Define the constructor
                    MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "", "()V", null, null);
                    mv.visitCode();
                    mv.visitVarInsn(Opcodes.ALOAD, 0);
                    mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "", "()V", false);
                    mv.visitInsn(Opcodes.RETURN);
                    mv.visitMaxs(1, 1);
                    mv.visitEnd();
                    // Define the main method
                    mv = cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
                    mv.visitCode();
                    mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
                    mv.visitLdcInsn("Hello, ASM!");
                    mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
                    mv.visitInsn(Opcodes.RETURN);
                    mv.visitMaxs(2, 2);
                    mv.visitEnd();
                    cw.visitEnd();
                    return cw.toByteArray();
                }
            }
            

            使用生成的类:

            public class HelloWorldRunner { public static void main(String[] args) { byte[] classData = HelloWorldGenerator.generateHelloWorldClass();
                    CustomClassLoader loader = new CustomClassLoader();
                    Class helloWorldClass = loader.defineClass("HelloWorld", classData);
                    try { // Invoke the main method
                        helloWorldClass.getMethod("main", String[].class).invoke(null, (Object) args);
                    } catch (Exception e) { e.printStackTrace();
                    }
                }
                // Custom class loader to define a class from byte array
                static class CustomClassLoader extends ClassLoader { public Class defineClass(String name, byte[] b) { return defineClass(name, b, 0, b.length);
                    }
                }
            }
            

            2. Java ASM主要组件

            JavaASM提供了的主要组件包括ClassReader、ClassWriter、ClassVisitor、MethodVIsitor。

            (1)ClassReader(类读取器)

            ClassReader用于读取已经存在的类文件的字节码,并以一种结构化的方式提供对类结构的访问。它能够解析类的结构,包括类的字段、方法和注解信息。

            accept(ClassVisitor classVisitor,int flags)
            //将类的结构传递给给定的ClassVisitor,flags参数用于指定读取选项
            

            (2)ClassWriter(类写入器)

            ClassWriter用于生产新的类文件的字节码。你可以通过添加新的字段、方法等,最终生成一个新的类字节码

            visit(int version, int access, String name, String signature, String superName, String[] interfaces)
            //开始访问一个类,指定类的版本、访问标志、类名、泛型签名、父类和实现的接口。
            visitMethod(int access, String name, String descriptor, String signature, String[] exceptions)
            //开始访问一个方法,指定方法的访问标志、方法名、描述符、泛型签名和可能抛出的异常。
            

            (3)ClassVisitor(类访问器):

            ClassVisitor是一个接口,用于遍历和修改类的结构。你可以自定义一个ClassVisitor,重写其方法,在访问类、方法、字段等时执行你的逻辑。

            visit(int version, int access, String name, String signature, String superName, String[] interfaces) 
            //开始访问一个类。
            visitMethod(int access, String name, String descriptor, String signature, String[] exceptions)
            //开始访问一个方法。
            visitField(int access, String name, String descriptor, String signature, Object value)
            //访问类的字段。
            

            (4)MethodVisitor(方法访问器)

            MethodVisitor 类似于 ClassVisitor,但专注于方法级别的访问和修改。你可以在访问方法、局部变量、指令等时插入你的操作。

            visitCode()
            //访问方法的代码开始部分。
            visitInsn(int opcode)
            //访问一个无操作指令。
            visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface)
            //访问方法调用指令
            

            这些组件共同构成了ASM的核心,使得开发人员能够以精细的方式操作Java字节码。通过创建自定义的 ClassVisitor 和 MethodVisitor,你可以在字节码层面执行各种操作,包括字节码增强、代码注入、AOP等。

            3. Java ASM使用组件准备

            创建一个简单的Java类

            public class MyClass { public void myMethod() { System.out.println("Original method");
                }
                public static void main(String[] args) { MyClass myClass = new MyClass();
                    myClass.myMethod();
                }
            }
            

            创建ClassReader

            要读取字节码,我们首先要创建一个ClassReader实例。ClassReader可以接收一个字节数组,表示已编译的Java类文件。

             //从文件系统中加载字节码
                    byte[] bytecode= Files.readAllBytes(Paths.get("/Users/jackchai/Desktop/Self-study-notes/Redis/redis_project/redis_test/target/classes/com/jack/redis/pojo/MyClass.class"));
                    //创建ClassReader实例
                    ClassReader classReader=new ClassReader(bytecode);
            

            使用ClassVisitor解析字节码

            要解析字节码,我们需要创建一个自定义的ClassVisitor实现。以下是一个简单的案例:

            public class MyClassVisitor extends ClassVisitor { //使用ASM,指定ASM的版本为5
                public MyClassVisitor(){ //调用父类的构造函数,指定ASM的版本为5
                    super(Opcodes.ASM5);
                }
                @Override
                public void visit(int version, int access, String name, String signature, String supername, String[] interfaces) { //打印类名
                    System.out.println("Class:"+s);
                    //调用父类的visit方法,以便继续处理类信息
                    super.visit(version, access, name, signature, supername, interfaces);
                }
                @Override
                public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { //打印方法名
                    System.out.println("Method:"+name);
                    return super.visitMethod(access, name, descriptor, signature, exceptions);
                }
            }
            

            Visit()方法参数说明:

            version:类文件的版本号,表示类文件的JDK版本。例如,JDK1.8的版本是52(0x34),JDK 11 对应的版本为55(0x37)

            access:类访问标志,表示类的访问权限和属性

            name:类的内部名称,如我们这个类就是com/jack/redis/pojo/MyClass

            sinature:类的范型签名,如果没有范型信息,则为null

            superName:父类的内部名。这里为java.lang.Object

            interfaces:类实现的接口的内部名称数组

            visitMethod()方法参数说明:

            access:方法访问标志

            name:放啊法的名称

            descriptor:方法的描述符,表示方法的参数类型和返回值类型

            signature:放啊法的范型信息

            exceptions:方法抛出的异常的内部名称数组

            public class LogInjector { public static void main(String[] args) throws IOException { //从文件系统中加载字节码
                    byte[] bytecode= Files.readAllBytes(Paths.get("/Users/jackchai/Desktop/Self-study-notes/Redis/redis_project/redis_test/target/classes/com/jack/redis/pojo/MyClass.class"));
                    //创建ClassReader实例
                    ClassReader classReader=new ClassReader(bytecode);
                    MyClassVisitor classVisitor=new MyClassVisitor();
                    //使用ClassReader的accept方法,将MyClassVisitor传递给ClassReader进行字节码解析
                    classReader.accept(classVisitor,0);
                }
            }
            

            上面解析字节码需要的基本组件都已经初始化完毕,下面开始真正解析字节码:

            4. 修改字节码

            • 添加、修改和删除字节码

              首先修改一下MyClass类

              public class MyClass { private String name;
                  private String type;
                  public void myMethod() { System.out.println("Original method");
                  }
                  public static void main(String[] args) { MyClass myClass = new MyClass();
                      myClass.myMethod();
                  }
              }
              

              要添加、修改或删除字节码,我们需要扩展ClassVisitor类并重写visitField方法。

              public class MyFieldClassVisitor extends ClassVisitor { public MyFieldClassVisitor(ClassVisitor classVisitor){ super(Opcodes.ASM5,classVisitor);
                  }
                  //visitField在访问类的字段时被调用,用于对字段进行修改
                  @Override
                  public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { //删除MyClass类的name字段
                      if("name".equals(s)){ //返回null表示删除字段
                          return null;
                      }
                      return super.visitField(access, name,descriptor, signature, value);
                  }
                  //visitEnd方法在访问类结尾时被调用,用于在类结尾添加新的字段
                  @Override
                  public void visitEnd() { //添加名为"newField"的字段
                      FieldVisitor fieldVisitor=super.visitField(Opcodes.ACC_PRIVATE,"newField","Ljava/lang/String;",null,null);
                      if(fieldVisitor!=null){ fieldVisitor.visitEnd();
                      }
                      super.visitEnd();
                  }
              }
              

              测试代码如下:

              public class LogInjector { public static void main(String[] args) { try { // 读取原始类的字节码
                          byte[] originalClassBytes = readClassBytes("MyClass.class");
                          // 使用 MyFieldClassVisitor 修改类
                          byte[] modifiedClassBytes = modifyClass(originalClassBytes);
                          // 将修改后的字节码保存到文件
                          saveToFile("MyModifiedClass.class", modifiedClassBytes);
                          // 运行修改后的类
                          runModifiedClass();
                      } catch (IOException e) { e.printStackTrace();
                      }
                  }
                  private static byte[] readClassBytes(String className) throws IOException { // 从类文件读取字节码
                      // 这里假设 MyClass.class 文件位于当前工程目录下
                      // 实际应用中,你可能需要根据你的工程结构调整路径
                      return Files.readAllBytes(Paths.get("/Users/jackchai/Desktop/Self-study-notes/Redis/redis_project/redis_test/target/classes/com/jack/redis/pojo/MyClass.class"));
                  }
                  private static byte[] modifyClass(byte[] originalClassBytes) { // 使用 MyFieldClassVisitor 修改类字节码
                      ClassReader classReader = new ClassReader(originalClassBytes);
                      ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
                      MyFieldClassVisitor myFieldClassVisitor = new MyFieldClassVisitor(classWriter);
                      classReader.accept(myFieldClassVisitor, ClassReader.SKIP_DEBUG);
                      // 返回修改后的字节码
                      return classWriter.toByteArray();
                  }
                  private static void saveToFile(String fileName, byte[] data) throws IOException { // 将字节码保存到文件
                      try (FileOutputStream fos = new FileOutputStream(fileName)) { fos.write(data);
                      }
                  }
                  private static void runModifiedClass() { // 运行修改后的类
                      MyClass modifiedClass = new MyClass();
                      System.out.println(modifiedClass);
                  }
              }
              

              可以看到修改后的class文件

              • 添加、修改和删除方法

                要添加、修改和删除方法,我们需要扩展ClassVisitor类并重写visitMethod方法。

                public class MyMethodClassVisitor extends ClassVisitor { public MyMethodClassVisitor(ClassVisitor classVisitor){ super(Opcodes.ASM5,classVisitor);
                    }
                    @Override
                    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { //删除myMethod方法
                        if("myMethod".equals(name)){ return null;
                        }
                        return super.visitMethod(access, name, descriptor, signature, exceptions);
                    }
                    @Override
                    public void visitEnd() { //添加名为"newMethod"方法
                        MethodVisitor methodVisitor=super.visitMethod(Opcodes.ACC_PUBLIC,"newMethod","()V",null,null);
                        if(methodVisitor!=null){ //开始访问方法的字节码
                            methodVisitor.visitCode();
                            //向方法字节码中添加Returen指令,表示方法返回
                            methodVisitor.visitInsn(Opcodes.RETURN);
                            //设置方法的最大操作树栈深度和局部变量大小,这里我们是一个空方法
                            methodVisitor.visitMaxs(0,0);
                            methodVisitor.visitEnd();
                        }
                        super.visitEnd();
                    }
                }
                

                下面我们来测试一下生成的字节码是怎么样子的

                public class LogInjector { public static void main(String[] args) { try { // 读取原始类的字节码
                            byte[] originalClassBytes = readClassBytes("MyClass.class");
                            // 使用 MyFieldClassVisitor 修改类
                            byte[] modifiedClassBytes = modifyClass(originalClassBytes);
                            // 将修改后的字节码保存到文件
                            saveToFile("MyModifiedClass.class", modifiedClassBytes);
                            // 运行修改后的类
                            runModifiedClass();
                        } catch (IOException e) { e.printStackTrace();
                        }
                    }
                    private static byte[] readClassBytes(String className) throws IOException { // 从类文件读取字节码
                        // 这里假设 MyClass.class 文件位于当前工程目录下
                        // 实际应用中,你可能需要根据你的工程结构调整路径
                        return Files.readAllBytes(Paths.get("/Users/jackchai/Desktop/Self-study-notes/Redis/redis_project/redis_test/target/classes/com/jack/redis/pojo/MyClass.class"));
                    }
                    private static byte[] modifyClass(byte[] originalClassBytes) { // 使用 MyFieldClassVisitor 修改类字节码
                        ClassReader classReader = new ClassReader(originalClassBytes);
                        ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
                        MyMethodClassVisitor myMethodClassVisitor = new MyMethodClassVisitor(classWriter);
                        classReader.accept(myMethodClassVisitor, ClassReader.SKIP_DEBUG);
                        // 返回修改后的字节码
                        return classWriter.toByteArray();
                    }
                    private static void saveToFile(String fileName, byte[] data) throws IOException { // 将字节码保存到文件
                        try (FileOutputStream fos = new FileOutputStream(fileName)) { fos.write(data);
                        }
                    }
                    private static void runModifiedClass() { // 运行修改后的类
                        MyClass modifiedClass = new MyClass();
                        System.out.println(modifiedClass);
                    }
                }
                

                成功删除了原来的myMethod方法,并添加了新的方法

                • 修改的方法中的指令

                  要修改方法中的指令,我们需要扩展MethodVisitor类并重写相应的visit方法。下面这个示例表示在每个方法调用前添加一条打印日志的指令:

                  public class MyMethodAdapter extends MethodVisitor { public MyMethodAdapter(MethodVisitor methodVisitor){ super(Opcodes.ASM5,methodVisitor);
                      }
                      @Override
                      public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) { //在方法调用前添加打印日志
                          mv.visitFieldInsn(Opcodes.GETSTATIC,"java/lang/System","out","Ljava/io/PrintStream");
                          mv.visitLdcInsn("Enterning method:"+name);
                          //熟悉字节码结构的都知道,这是在向字节码文件中插入内容
                          mv.visitMethodInsn(Opcodes.INVOKESPECIAL,"java/io/PringStream","println","(Ljava/lang/String;)V",false);
                          super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
                      }
                  }
                  

                  要应用这个方法适配器,我们需要在自定义的ClassVisitor实现中重写visitMethod方法

                  public class MyMethodLoggerClassVisitor extends ClassVisitor { public MyMethodLoggerClassVisitor(ClassVisitor classVisitor){ super(Opcodes.ASM5,classVisitor);
                      }
                      @Override
                      public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor methodVisitor=super.visitMethod(access,name,descriptor,signature,exceptions);
                          return new MyMethodAdapter(methodVisitor);
                      }
                  }
                  

                  测试方法如下:

                  public class LogInjector { public static void main(String[] args) { try { // 读取原始类的字节码
                              byte[] originalClassBytes = readClassBytes("MyClass.class");
                              // 使用 MyFieldClassVisitor 修改类
                              byte[] modifiedClassBytes = modifyClass(originalClassBytes);
                              // 将修改后的字节码保存到文件
                              saveToFile("MyModifiedClass.class", modifiedClassBytes);
                              // 运行修改后的类
                              runModifiedClass();
                          } catch (IOException e) { e.printStackTrace();
                          }
                      }
                      private static byte[] readClassBytes(String className) throws IOException { // 从类文件读取字节码
                          // 这里假设 MyClass.class 文件位于当前工程目录下
                          // 实际应用中,你可能需要根据你的工程结构调整路径
                          return Files.readAllBytes(Paths.get("/Users/jackchai/Desktop/Self-study-notes/Redis/redis_project/redis_test/target/classes/com/jack/redis/pojo/MyClass.class"));
                      }
                      private static byte[] modifyClass(byte[] originalClassBytes) { // 使用 MyFieldClassVisitor 修改类字节码
                          ClassReader classReader = new ClassReader(originalClassBytes);
                          ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
                          MyMethodLoggerClassVisitor myMethodClassVisitor = new MyMethodLoggerClassVisitor(classWriter);
                          classReader.accept(myMethodClassVisitor, ClassReader.SKIP_DEBUG);
                          // 返回修改后的字节码
                          return classWriter.toByteArray();
                      }
                      private static void saveToFile(String fileName, byte[] data) throws IOException { // 将字节码保存到文件
                          try (FileOutputStream fos = new FileOutputStream(fileName)) { fos.write(data);
                          }
                      }
                      private static void runModifiedClass() { // 运行修改后的类
                          MyClass modifiedClass = new MyClass();
                          System.out.println(modifiedClass);
                      }
                  }
                  

                  可以看出日志添加成功

                  • 生成新的字节码

                    在修改字节码后,我们需要使用ClassWriter (它是ClassVisitor的子类)生成新的字节码。上面提供的几个测试代码都用到了ClassWriter

                     private static byte[] modifyClass(byte[] originalClassBytes) { // 使用 MyFieldClassVisitor 修改类字节码
                            ClassReader classReader = new ClassReader(originalClassBytes);
                            ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
                            MyMethodLoggerClassVisitor myMethodClassVisitor = new MyMethodLoggerClassVisitor(classWriter);
                            classReader.accept(myMethodClassVisitor, ClassReader.SKIP_DEBUG);
                            // 返回修改后的字节码
                            return classWriter.toByteArray();
                        }
                    

                    三、Java ASM高级技巧

                    1. 自定义类加载器

                    要实现自定义类加载器,我们需要扩展Java标准裤的ClassLoader,并重写findClass方法。以下展示了如何实现一个简单的自定义类加载器,它使用Java ASM修改类字节码后加载类:

                    import java.io.IOException;
                    import java.io.InputStream;
                    import java.nio.ByteBuffer;
                    import org.objectweb.asm.ClassReader;
                    import org.objectweb.asm.ClassWriter;
                    public class MyClassLoader extends ClassLoader { @Override
                        protected Class findClass(String name) throws ClassNotFoundException { try { // 加载原始字节码
                                String resourceName = name.replace('.', '/').concat(".class");
                                InputStream is = getClass().getClassLoader().getResourceAsStream(resourceName);
                                byte[] originalBytecode = is.readAllBytes();
                                // 使用 Java ASM 修改字节码
                                byte[] modifiedBytecode = modifyBytecode(originalBytecode);
                                // 使用修改后的字节码定义类
                                ByteBuffer byteBuffer = ByteBuffer.wrap(modifiedBytecode);
                                return defineClass(name, byteBuffer, null);
                            } catch (IOException e) { throw new ClassNotFoundException("Failed to load class " + name, e);
                            }
                        }
                        private byte[] modifyBytecode(byte[] originalBytecode) { // 创建 ClassReader 和 ClassWriter
                            ClassReader classReader = new ClassReader(originalBytecode);
                            ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
                            // 创建自定义的 ClassVisitor(执行切入动作)
                            MyMethodLoggerClassVisitor myMethodLoggerClassVisitor = new MyMethodLoggerClassVisitor(classWriter);
                            // 使用 ClassReader 遍历字节码,应用自定义的 ClassVisitor
                            classReader.accept(myMethodLoggerClassVisitor, ClassReader.EXPAND_FRAMES);
                            // 返回修改后的字节码
                            return classWriter.toByteArray();
                        }
                    }
                    

                    2. 动态代理

                    动态代理是一种常用的设计模式,可以在运行时动态地为对象生成代理。使用 Java ASM,我们可以生成字节码来实现动态代理。以下是一个简单的动态代理示例,它实现了一个基于接口的代理:

                    import java.lang.reflect.InvocationHandler;
                    import java.lang.reflect.Method;
                    import org.objectweb.asm.ClassWriter;
                    import org.objectweb.asm.MethodVisitor;
                    import org.objectweb.asm.Type;
                    public class MyDynamicProxy { public static  T createProxy(Class interfaceClass, InvocationHandler handler) { // 创建 ClassWriter
                            ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
                            // 定义代理类,实现指定的接口
                            String proxyClassName = interfaceClass.getName() + "$Proxy";
                            String proxyClassInternalName = proxyClassName.replace('.', '/');
                            classWriter.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, proxyClassInternalName, null, "java/lang/Object", new String[]{Type.getInternalName(interfaceClass)});
                            // 实现代理类的构造方法
                            MethodVisitor constructorVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "", "()V", null, null);
                            constructorVisitor.visitCode();
                                constructorVisitor.visitVarInsn(Opcodes.ALOAD, 0);
                                constructorVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "", "()V", false);
                                constructorVisitor.visitInsn(Opcodes.RETURN);
                                constructorVisitor.visitMaxs(1, 1);
                                constructorVisitor.visitEnd();
                            // 实现接口的所有方法
                            for (Method method : interfaceClass.getDeclaredMethods()) { String methodName = method.getName();
                                String methodDescriptor = Type.getMethodDescriptor(method);
                                MethodVisitor methodVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, methodName, methodDescriptor, null, null);
                                // 在代理方法中调用 InvocationHandler 的 invoke 方法
                                methodVisitor.visitCode();
                                methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
                                methodVisitor.visitFieldInsn(Opcodes.GETFIELD, proxyClassInternalName, "handler", "Ljava/lang/reflect/InvocationHandler;");
                                methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
                                methodVisitor.visitLdcInsn(Type.getType(interfaceClass));
                                methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/reflect/Method", "valueOf", "(Ljava/lang/Class;)Ljava/lang/reflect/Method;", false);
                                methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
                                methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/lang/reflect/InvocationHandler", "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;", true);
                                methodVisitor.visitInsn(Opcodes.ARETURN);
                                methodVisitor.visitMaxs(4, 2);
                                methodVisitor.visitEnd();
                            }
                            classWriter.visitEnd();
                            // 使用自定义类加载器加载代理类
                            byte[] proxyClassBytecode = classWriter.toByteArray();
                            MyClassLoader myClassLoader = new MyClassLoader();
                            Class proxyClass = myClassLoader.defineClass(proxyClassName, ByteBuffer.wrap(proxyClassBytecode), null);
                            // 创建代理类实例,并设置 InvocationHandler
                            try { T proxyInstance = (T) proxyClass.getConstructor().newInstance();
                                proxyClass.getField("handler").set(proxyInstance, handler);
                                return proxyInstance;
                            } catch (ReflectiveOperationException e) { throw new RuntimeException("Failed to create proxy instance", e);
                            }
                            }
                    }
                    

                    可以使用MyDynamicProxy为接口创建动态代理

                    import java.lang.reflect.InvocationHandler;
                    import java.lang.reflect.Method;
                    import org.objectweb.asm.ClassWriter;
                    import org.objectweb.asm.MethodVisitor;
                    import org.objectweb.asm.Type;
                    public class MyDynamicProxy { public static  T createProxy(Class interfaceClass, InvocationHandler handler) { // 创建 ClassWriter
                            ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
                            // 定义代理类,实现指定的接口
                            String proxyClassName = interfaceClass.getName() + "$Proxy";
                            String proxyClassInternalName = proxyClassName.replace('.', '/');
                            classWriter.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, proxyClassInternalName, null, "java/lang/Object", new String[]{Type.getInternalName(interfaceClass)});
                            // 实现代理类的构造方法
                            MethodVisitor constructorVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "", "()V", null, null);
                            constructorVisitor.visitCode();
                                constructorVisitor.visitVarInsn(Opcodes.ALOAD, 0);
                                constructorVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "", "()V", false);
                                constructorVisitor.visitInsn(Opcodes.RETURN);
                                constructorVisitor.visitMaxs(1, 1);
                                constructorVisitor.visitEnd();
                            // 实现接口的所有方法
                            for (Method method : interfaceClass.getDeclaredMethods()) { String methodName = method.getName();
                                String methodDescriptor = Type.getMethodDescriptor(method);
                                MethodVisitor methodVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, methodName, methodDescriptor, null, null);
                                // 在代理方法中调用 InvocationHandler 的 invoke 方法
                                methodVisitor.visitCode();
                                methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
                                methodVisitor.visitFieldInsn(Opcodes.GETFIELD, proxyClassInternalName, "handler", "Ljava/lang/reflect/InvocationHandler;");
                                methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
                                methodVisitor.visitLdcInsn(Type.getType(interfaceClass));
                                methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/reflect/Method", "valueOf", "(Ljava/lang/Class;)Ljava/lang/reflect/Method;", false);
                                methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
                                methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/lang/reflect/InvocationHandler", "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;", true);
                                methodVisitor.visitInsn(Opcodes.ARETURN);
                                methodVisitor.visitMaxs(4, 2);
                                methodVisitor.visitEnd();
                            }
                            classWriter.visitEnd();
                            // 使用自定义类加载器加载代理类
                            byte[] proxyClassBytecode = classWriter.toByteArray();
                            MyClassLoader myClassLoader = new MyClassLoader();
                            Class proxyClass = myClassLoader.defineClass(proxyClassName, ByteBuffer.wrap(proxyClassBytecode), null);
                            // 创建代理类实例,并设置 InvocationHandler
                            try { T proxyInstance = (T) proxyClass.getConstructor().newInstance();
                                proxyClass.getField("handler").set(proxyInstance, handler);
                                return proxyInstance;
                            } catch (ReflectiveOperationException e) { throw new RuntimeException("Failed to create proxy instance", e);
                            }
                            }
                    }