文章目录
- 一、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的应用场景
- 代码分析和优化:通过ASM,我们可以对字节码进行深入分析,以找出潜在的性能瓶颈或者执行特定的优化
- 动态代理和AOP(面向切面编程):ASM可以用于创建动态代理或实现AOP框架,从而实现运行时的行为修改
- 自定义类加载器:通过ASM,我们可以实现自定义类加载器,以支持独特的类加载策略和实现热加载等功能
- 安全审计: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); } } }
- 生成新的字节码
- 修改的方法中的指令
- 添加、修改和删除方法