学习条件:学习Java ASM之前你必须对JVM指令有一定的了解,理解栈桢、操作数栈、局部变量表的概念,必须能看必要的JVM,所以建议先学习JVM指令!先学懂作者的《Java字节码操作之ASM创建class》再来看这篇文章!
一、ASM可以做什么?
简单的说Java ASM是一个非常底层的字节码操作框架,它可以创建一个全新的Java class文件也可以修改现有的class文件对象,是Java开发中非常核心的技术点。spring、Mybatis、Hibernate等现在Java中的框架底层都是用动态代理实现的核心功能,而动态代理的核心技术点就是ASM,所以说没有ASM也就没有这些框架!
二、如何使用ASM修改一个class文件对象呢?
目标:修改《Java字节码操作之ASM创建class》中创建的class文件对象修改结果如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.zjw;
import java.util.ArrayList;
public class HelloAsm {
private String name;
private long age1;
public static long num;
public static final String ADMIN_NAME = "赵进伟";
public HelloAsm() {
}
public int method(String var1) {
System.out.println("method start");
System.out.println("method ing");
ArrayList var2 = new ArrayList();
StringBuilder var3 = new StringBuilder();
var3.append("Hello Java Asm StringBuilder!");
long var4 = System.nanoTime();
int var10004 = 10 + 11;
System.out.println("method end");
return var10004;
}
public static void staticMethod(String var0) {
System.out.println("Hello Java Asm!");
}
}
1、创建ClassReader和ClassWriter对象
ClassReader classReader = new ClassReader(orgBytes);
ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
2、创建自定义ClassVisitor
ClassVisitor cv = new MyClassVisitor(classWriter);
//MyClassVisitor的结构
public class MyClassVisitor extends ClassVisitor {
public MyClassVisitor(ClassVisitor cv) {
super(ASM5, cv);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
MethodVisitor methodVisitor = super.visitMethod(access, name, desc, signature, exceptions);
if (name.equals("method")) {
return new MyMethodVisitor(methodVisitor);
} else {
return methodVisitor;
}
}
@Override
public FieldVisitor visitField(int i, String s, String s1, String s2, Object o) {
if (s.equals("age")) {
return super.visitField(i, "age1", s1, s2, o);
}
return super.visitField(i, s, s1, s2, o);
}
}
//MyMethodVisitor的结构
public class MyMethodVisitor extends MethodVisitor {
public MyMethodVisitor(MethodVisitor mv) {
super(ASM5, mv);
}
//此方法在目标方法调用之前调用,所以前置操作可以在这处理
@Override
public void visitCode() {
mv.visitCode();
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitLdcInsn("method start");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
}
@Override
public void visitInsn(int opcode) {
//此方法可以获取方法中每一条指令的操作类型,被访问多次
//如应在方法结尾处添加新指令,则应判断:
if (opcode == Opcodes.IRETURN) {
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitLdcInsn("method end");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
}
super.visitInsn(opcode);
}
@Override
public void visitMaxs(int maxStack, int maxLocals) {
super.visitMaxs(maxStack + 1, maxLocals);
}
}
3、调用accept和toByteArray生成class字节数组
classReader.accept(cv, 0);
byte[] bytes = classWriter.toByteArray();
4、将二进制流写到本地磁盘上
//将二进制流写到本地磁盘上
FileOutputStream fos = null;
try {
fos = new FileOutputStream("HelloAsm.class");
fos.write(bytes);
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
到此ASM修改一个class就完成了!
作者整理资料也是查阅了N篇文章,对你有帮助请点个赞吧!
需要作者项目源码欢迎在评论区留言!
相关推荐:Java反射,Java自定义注解开发