ASM是操作类字节码的工具包,提供ClassVisitor、FieldVisitor、MethodVisito等访问方法
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>6.2</version>
</dependency>
public class AsmTest {
public static void main(String[] args) throws Exception {
b();
}
/**
* asm创建类,方法,字段
* @throws Exception
*/
public static void a() throws Exception{
String className = "classhandle.AsmClass";
String signature = "L" + className.replace(".", "/") + ";";
// 创建类,参数值为0,需要自己计算方法的局部变量表和操作数栈的大小,所以在创建方法时,需要调用方法的visitMaxs方法
//ClassWriter.COMPUTE_MAXS自动计算
ClassWriter classWriter = new ClassWriter(0);
/** version:指定类文件结构的版本号;
access:指定类的访问标志,如public、final等;
name:指定类的名称(内部类名),如“java/lang/String”;
signature:类的类型签名,如“Ljava/lang/String;”;
superName:继承的父类名称。除Object类外,所有的类都必须有父类;
interfaces:该类需要实现的接口*/
classWriter.visit(Opcodes.V1_8, 0x0001,
className.replace(".", "/"),
signature,
Object.class.getName().replace(".", "/"),
null);
/**
*操作方法
access:方法的访问标志,如public、static等;
name:方法的名称(内部类名);
descriptor:方法的描述符,如“()V”;
signature:方法签名,可以为空;
exceptions:该方法可能抛出的受检异常的类型内部名称,可以为空。*/
MethodVisitor methodVisitor = classWriter.visitMethod(0x0001,"<init>","()V",null,null);
/**
MethodVisitor接口定义的几个常用API:
visitCode:访问方法的Code属性,实际上也是一个空方法,什么事情也不做;
visitMaxs:用于设置方法的局部变量表与操作数栈的大小;
MethodVisitor接口提供的编写字节码指令相关的API:
visitInsn:往Code属性的code数组中添加一条无操作数的字节码指令,如dup指令、aload_0指令等;
visitVarInsn:往Code属性的code数组中添加一条需要一个操作数的字节码指令,如aload指令;
visitFieldInsn:往Code属性的code数组中添加一条访问字段的字节码指令,用于添加putfield、getfield、putstatic、getstatic指令;
visitTypeInsn:往Code属性的code数组中添加一条操作数为常量池中某个CONSTANT_Class_info常量的索引的字节码指令,如new指令;
visitMethodInsn:往Code属性的code数组中添加一条调用方法的字节码指令,如invokevirtual指令。
*/
methodVisitor.visitCode();
// 调用父类构造器
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
/* *
visitMethodInsn方法的各参数解析:
opcode:字节码指令的操作码,如invokesepcial指令的操作码十进制的值为183;
owner:类的内部类型名称,如“java/lang/Object”;
name:方法名称,如“”;
descriptor:方法描述符,如“()V”;
isInterface:是否是接口,使用invokeinterface指令才传true,其它都传false。
*/
methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL,"java/lang/Object","<init>", "()V", false);
// 添加一条返回指令
methodVisitor.visitInsn(Opcodes.RETURN);
// 设置操作数栈和局部变量表大小
methodVisitor.visitMaxs(1,1);
classWriter.visitEnd();
/**字段操作
visitField方法的各参数说明:
access:字段的访问标志,如public、final、static;
name:字段的名称;
descriptor:字段的类型描述符,如”Ljava/lang/String;”;
signature:字段的类型签名;
value:字段的初始值,此参数只用于静态字段,如接口中声明的字段或类中声明的静态常量字段,并且类型必须是基本数据类型或String类型
如:
FieldVisitor fieldVisitor = classWriter.visitField(ACC_PRIVATE,"name", "Ljava/lang/String;", null, null);
fieldVisitor.visitAnnotation("Llombok/Getter;", false);
*/
// 获取生成的class的字节数组
byte[] byteCode = classWriter.toByteArray();
File file = new File("D:\\util\\mypro\\src\\main\\java\\classhandle\\"
+className.substring(className.lastIndexOf(".")+1)+ ".class");
if ((!file.exists() || file.delete()) && file.createNewFile()) {
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(byteCode);
}
}
}
/**
* asm改写类并改写方法
* 改写一个类首先需要获取到一个类的字节数组。可通过使用ClassReader读取并解析获取到一个类的字节数组,
* 再将解析后的字节数组交给ClassWriter去改写
* @throws Exception
*/
public static void b() throws Exception{
String className = "classhandle.AsmClass";
ClassReader classReader = new ClassReader(className);
ClassWriter classWriter = new ClassWriter(0);
classReader.accept(classWriter, 0);
FieldVisitor fieldVisitor = classWriter.visitField(Opcodes.ACC_PRIVATE,
"name", "Ljava/lang/String;", null, null);
fieldVisitor.visitAnnotation("Llombok/Getter;", false);
byte[] byteCode = classWriter.toByteArray();
File file = new File("D:\\util\\mypro\\src\\main\\java\\classhandle\\"
+className.substring(className.lastIndexOf(".")+1)+ ".class");
if ((!file.exists() || file.delete()) && file.createNewFile()) {
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(byteCode);
}
}
}
/**
* 实现接口
* @throws Exception
*/
public static void c() throws Exception{
// 创建的类的类名
String implClassName = BaseByteCodeHandler.class.getName() + "Impl";
ClassWriter cw = new ClassWriter(0);
// 设置class文件结构的版本号、类名、类签名、父类、实现的接口
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC,
implClassName.replace(".", "/"),
null,
Type.getInternalName(Object.class),
new String[]{Type.getInternalName(BaseByteCodeHandler.class)});
// 创建asyHello方法
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "order",
"()V", null, null);
// 插入输出"hello word!"的字节码指令
mv.visitFieldInsn(Opcodes.GETSTATIC,
Type.getInternalName(System.class),
"out",
Type.getDescriptor(System.out.getClass()));
mv.visitLdcInsn("hello word!");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
Type.getInternalName(System.out.getClass()),
"println",
"(Ljava/lang/String;)V", false);
// void方法也需要有return指令
mv.visitInsn(Opcodes.RETURN);
// 设置局部变量表和操作数栈的大小
mv.visitMaxs(2,1);
// 获取生成的类的字节数组
byte[] byteCode = cw.toByteArray();
}
/**
* 创建子类重写父类方法
* @throws Exception
*/
public static void d() throws Exception{
// 创建的类的类名
String subClassName = AsmTest.class.getName()
+"Sub";
ClassWriter cw = new ClassWriter(0);
// 设置class文件结构的版本号、类名、类签名、父类、实现的接口
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC,
subClassName.replace(".", "/"),
null,
Type.getInternalName(AsmTest.class),
null);
// 创建方法
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "d",
"()V", null, null);
// 调用父类的方法
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL,
Type.getInternalName(AsmTest.class),
"d",
"()V", false);
// 插入输出"SubClass sayHello"的字节码指令
mv.visitFieldInsn(Opcodes.GETSTATIC,
Type.getInternalName(System.class),
"out",
Type.getDescriptor(System.out.getClass()));
mv.visitLdcInsn("SubClass sayHello");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
Type.getInternalName(System.out.getClass()),
"println",
"(Ljava/lang/String;)V", false);
mv.visitInsn(Opcodes.RETURN);
// 设置局部变量表和操作数栈的大小
mv.visitMaxs(2, 1);
// 获取生成的类的字节数组
byte[] byteCode = cw.toByteArray();
}
}
版权声明:本文为wste3567原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。