ASM Java字节码操作框架入门学习 输出Hello World
1.类信息
package org.example;public class Hello {public void say(){System.out.println("hello world");}}
查看字节码信息
//动态设置栈大小ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);//设置类的基本信息classWriter.visit(V1_8, //设置JDK版本,ACC_PUBLIC, // 设置权限修饰符,"Hello", //新类的权限定类名,null,//泛型"java/lang/Object", // 父类null//实现的接口);
2.无参构造方法
0 aload_0
1 invokespecial #1 <java/lang/Object.<init> : ()V>
4 return
MethodVisitor constructor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);constructor.visitVarInsn(Opcodes.ALOAD, 0); //操作局部变量表 局部变量表第一个存放thisconstructor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);constructor.visitInsn(Opcodes.RETURN); // 调用 return 指令constructor.visitMaxs(1, 1);//方法的最大栈大小 方法的最大局部变量数constructor.visitEnd(); //方法结束
3.say 方法
0 getstatic #2 <java/lang/System.out : Ljava/io/PrintStream;>
3 ldc #3 <hello world>
5 invokevirtual #4 <java/io/PrintStream.println : (Ljava/lang/String;)V>
8 return
//添加say方法MethodVisitor methodVisitor = classWriter.visitMethod(ACC_PUBLIC,//方法修饰符号"say",//方法名"()V",//方法的描述符,用于描述方法的参数类型和返回值类型null,//方法泛型null//可能抛出的异常);//获取System.outmethodVisitor.visitFieldInsn(Opcodes.GETSTATIC,//字段的类型"java/lang/System",//字段所属类的全限定类名"out",//指定字段"Ljava/io/PrintStream;"//字段描述信息);//加载常量methodVisitor.visitLdcInsn("hello world");methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL,//方法调用的操作码 "java/io/PrintStream",//方法的权限定类名"println",//方法名"(Ljava/lang/String;)V", //方法修饰符false);//方法调用者是否是接口methodVisitor.visitInsn(Opcodes.RETURN);methodVisitor.visitMaxs(2, 0);methodVisitor.visitEnd();
4.完成类的定义并创建实例调用目标方法
// 完成类的定义classWriter.visitEnd();// 将生成的字节码写入文件或加载到内存中byte[] bytecode = classWriter.toByteArray();ClassLoader classLoader = new ClassLoader() {@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {return defineClass(name, bytecode, 0, bytecode.length);}};// 加载并实例化Hello类Class<?> helloClass = classLoader.loadClass("Hello");Object helloObject = helloClass.getDeclaredConstructor().newInstance();// 调用say方法helloClass.getMethod("say").invoke(helloObject);
5.相关JVM指令
方法调用
- invokestatic:用于调用静态方法。该指令会根据方法的类名、方法名和方法描述符进行方法查找和调用。
- invokespecial:用于调用私有方法、构造方法和父类方法。该指令会根据方法的类名、方法名和方法描述符进行方法查找和调用。
- invokevirtual:用于调用实例方法。该指令会根据对象的类型和方法的签名进行方法查找和调用。
- invokeinterface:用于调用接口方法。该指令会根据接口的类型和方法的签名进行方法查找和调用。
- invokedynamic:用于调用动态方法。该指令会通过调用动态绑定方法来实现方法的调用
加载常量或数字
- ldc:将常量(包括字符串、整数、浮点数等)加载到操作数栈上。
- ldc_w:与ldc类似,但用于加载较大的常量(超过65535个字节)。
- bipush:将一个字节大小的整数常量(-128到127之间)加载到操作数栈上。
- sipush:将一个短整型常量(-32768到32767之间)加载到操作数栈上。
- iconst_:将整数常量(-1到5之间)加载到操作数栈上,其中为0到5之间的数字。 _
- fconst:将浮点数常量(0.0、1.0和2.0)加载到操作数栈上,其中为0到2之间的数字。
- dconst_:将双精度浮点数常量(0.0和1.0)加载到操作数栈上,其中为0或1。 _
- _ lconst_:将长整型常量(0和1)加载到操作数栈上,其中为0或1