前些日工作中需要改一个jar包中的程序代码,想了很多方法 包括直接修改、动态代理等等,但都效果不好。最后无意中发现了ASM这个框架,感觉正是我需要的。研究几日,用到一些基础功能就实现了所要效果,所以写出来给大家共享,自己忘了也好参考参考.下面是ASM一些基本介绍,就当是抛砖引玉了~~其中参考了几篇其他iteye朋友的文章,主要是JVM、字节码和类加载方面的东西,有助于更好的理解ASM。这里我主要说说ASM的东西,其他的大家去看看吧。
一、什么是ASM
ASM是一个java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
二、ASM核心类介绍:
①ClassReader类:这个类会提供你要转变的类的字节数组,它的accept方法,接受一个具体的ClassVisitor,并调用实现中具体的visit,visitSource, visitOuterClass, visitAnnotation, visitAttribute, visitInnerClass, visitField,visitMethod和 visitEnd方法。
所需开发包:asmSDK

public class Person {
public final String sleep() {
try {
Thread.sleep(2);//沉睡两秒
} catch (InterruptedException e) {
e.printStackTrace();
}
return "123";
}
}
ModifyMethodClassAdapter.java代码如下:
package com.asm1;
import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
public class ModifyMethodClassAdapter extends ClassAdapter {
public ModifyMethodClassAdapter(ClassVisitor cv) {
super(cv);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
//(Ljava/lang/String;[BII)Ljava/lang/Class;
//(Ljava/lang/String;[BIILjava/security/ProtectionDomain;)Ljava/lang/Class;
int a=10;
if (name.equals("sleep")&&desc.equals("()Ljava/lang/String;")) {
return new ModifyMethod(super.visitMethod(access, name, desc,
signature, exceptions), access, name, desc);
}
//System.out.println(a);
return super.visitMethod(access, name, desc, signature, exceptions);
}
@Override
public void visitEnd() {
cv.visitField(Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC, "timer", "J",
null, null);
}
}
注意name.equals("sleep")&&desc.equals("()Ljava/lang/String;")这行,ASM会根据方法名和方法描述来查找方法,下面是方法描述在类中和二进制中的对应关系
|
package com.asm1;
import org.objectweb.asm.MethodAdapter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
public class ModifyMethod extends MethodAdapter {
public ModifyMethod(MethodVisitor mv, int access, String name, String desc) {
super(mv);
}
@Override
public void visitCode() {
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out",
"Ljava/io/PrintStream;");
mv.visitLdcInsn("我是赛亚人");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream",
"println", "(Ljava/lang/String;)V");
}
}
代码中各方法
visitLdcInsn: 将常量池中的字符串常量加载进栈顶
visitMethodInsn:访问方法的指令,该指令负责调用一个方法
public class test {
public static void main(String[] args) {
System.out.println("我是赛亚人");
}
}

该文件是class的字节码指令集,可查看虚拟机字节码指令表来进一步了解。
ClassReader classReader = new ClassReader("com.asm1.Person"); //com.asm1.Person
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
ClassAdapter classAdapter = new ModifyMethodClassAdapter(classWriter);
classReader.accept(classAdapter, ClassReader.SKIP_DEBUG);
byte[] classFile = classWriter.toByteArray();
File f = new File("F:\\abc\\aaa.class");
FileOutputStream fs = new FileOutputStream(f);
fs.write(classFile);
fs.close();
System.out.println("success");
运行该程序,在abc文件夹下会生成一个aaa.class文件,使用反编译软件打开该文件如下:

如图所示,成功添加了一行打印语句。