ASM是Java中比较流行的用来读写字节码的类库,用来基于字节码层面对代码进行分析和转换。在读写的过程中可以加入自定义的逻辑以增强或修改原来已编译好的字节码,比如CGLIB用它来实现动态代理。ASM被设计用于在运行时对Java类进行生成和转换,当然也包括离线处理。ASM短小精悍、且速度很快,从而避免在运行时动态生成字节码或转换时对程序速度的影响,又因为它体积小巧,可以在很多内存受限的环境中使用。
ClassWriter的使用
ClassVisitor是用来生成asm和改变字节码的,ClassVisitor是一
个访问字节码的框架,其对字节码的创建和修改主要是通过其内部的ClassVisitor具体实现来代理的
ClassWriter:ClassWriter 是ClassVisitor的一个实现类
ClassWriter提供了一类visit方法来编写类。使用visit方法开始编写,visitEnd方法结束编写。
具体的来说,此类visit方法能够为需要修改的类添加属性和方法。例如,visitMethod可以为类添加方法,visitAttribute可以为类添加属性。
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public static void main(String[] args) throws IOException {
ClassWriter cs = new ClassWriter(0);
//通过vist确定类的同步信息 java版本号 类修饰符 类的权限定名
cs.visit(Opcodes.V1_8,Opcodes.ACC_PUBLIC,"HZYziyi",null, "java/lang/Object",null);
//构造函数
MethodVisitor mv = cs.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
// 定义code方法
MethodVisitor methodVisitor = cs.visitMethod(Opcodes.ACC_PUBLIC, "code", "()V", null, null);
methodVisitor.visitCode();
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
methodVisitor.visitLdcInsn("I'm a哈哈哈 Duck,Just Coding.....");
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
methodVisitor.visitInsn(Opcodes.RETURN);
methodVisitor.visitMaxs(2, 2);
methodVisitor.visitEnd();
cs.visitEnd();
// 使classWriter类已经完成
// 将classWriter转换成字节数组写到文件里面去
byte[] data = cs.toByteArray();
File file = new File("./HZYziyi.class");
FileOutputStream fout = new FileOutputStream(file);
fout.write(data);
fout.close();
}
运行结束和可以看到目录下生成了一个HZYziyi.class文件,可以看到其中的内容:
自定义一个ClassVisitor
写一个类继承ClassVisitor
public class ClassChangeAdapter extends ClassVisitor {
public ClassChangeAdapter(ClassVisitor classVisitor) {
super(ASM4, classVisitor);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
// 如果当前访问到main方法就移除掉
if(name.equals("main")) {
return null;
}
MethodVisitor mv =super.visitMethod(access, name,
desc, signature, exceptions);
MethodChangeAdaper methodChangeAdaper = new MethodChangeAdaper(ASM4, mv, access, name, desc);
return methodChangeAdaper;
}
}
自定义MethodChangeAdaper类继承AdviceAdapter
public class MethodChangeAdaper extends AdviceAdapter {
/**
* Creates a new {@link AdviceAdapter}.
*
* @param api the ASM API version implemented by this visitor. Must be one
* of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
* @param mv the method visitor to which this adapter delegates calls.
* @param access the method's access flags (see {@link Opcodes}).
* @param name the method's name.
* @param desc the method's descriptor (see {@link Type Type}).
*/
protected MethodChangeAdaper(int api, MethodVisitor mv, int access, String name, String desc) {
super(api, mv, access, name, desc);
}
@Override
public void visitCode() {
super.visitCode();
//调用静态方法
mv.visitMethodInsn(INVOKESTATIC, "com/tuling/cglib/proxy/DaoAnotherProxy", "before", "()V", false);
// 创建新的局部变量表索引
// 将int型2推送至操作数栈顶
mv.visitInsn(ICONST_2);
/// 创建新的局部变量表索引
int newLocal = super.newLocal(jdk.internal.org.objectweb.asm.Type.INT_TYPE);
mv.visitIntInsn(ISTORE, newLocal);
mv.visitIincInsn(newLocal, 1);
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitIntInsn(ILOAD, newLocal);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V", false);
mv.visitMethodInsn(INVOKESTATIC, "com/tuling/cglib/proxy/DaoAnotherProxy", "after", "()V", false);
}
}
在mv.visitMethodInsn(INVOKESTATIC, “com/tuling/cglib/proxy/DaoAnotherProxy”, “before”, “()V”, false);
这句话中调用了自己的包下这个DaoAnotherProxy类的before方法
其中DaoAnotherProxy 类:
其中public class DaoAnotherProxy {
public static void before(){
System.out.println("前置通知!");
}
public static void after(){
System.out.println("后置通知!");
}
}
主类:
public class ClassChangeMethodTest {
public static void main(String[] args) throws Exception {
ClassWriter cw = new ClassWriter(COMPUTE_MAXS);
ClassReader cr = new ClassReader(TestmyQuestion.class.getName());
ClassChangeAdapter adapter=new ClassChangeAdapter(cw);
cr.accept(adapter,ClassReader.SKIP_FRAMES);
byte[] bytes = cw.toByteArray();
File file = new File("./TestmyQuestion.class");
FileOutputStream fout = new FileOutputStream(file);
fout.write(bytes);
fout.close();
MyClassLoader classLoader=new MyClassLoader();
Class loadedClass = classLoader.defineClass(TestmyQuestion.class.getName(), bytes);
loadedClass.newInstance();
//创建由此Class对象表示的类的新实例。 该类被实例化为一个带有空参数列表的new表达式。 如果尚未初始化该类,则将其初始化
}
}
其中TestmyQuestion 类:
public class TestmyQuestion {
public static void main(String[] args) {
int i=2;
i++;
System.out.println(i);
}
}
上面的程序运行以后可以看到目录下生成了一个class文件TestmyQuestion.class:
import com.tuling.cglib.proxy.DaoAnotherProxy;
public class TestmyQuestion {
public TestmyQuestion() {
DaoAnotherProxy.before();
byte var1 = 2;
int var2 = var1 + 1;
System.out.println(var2);
DaoAnotherProxy.after();
super();
}
}
这即是asm生成的class文件
输出:
enhancer的使用
自定义MethodInterceptor :
/**
* 创建一个Dao代理
*/
public class DaoProxy implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("begin intercept");
//invokeSuper方法调用目标类的方法
proxy.invokeSuper(obj, args);
System.out.println("end intercept");
return obj;
}
}
自定义CallbackFilter
import net.sf.cglib.proxy.CallbackFilter;
import java.lang.reflect.Method;
/**
* 返回数值表示顺序
*/
public class DaoFilter implements CallbackFilter {
@Override
public int accept(Method method) {
System.out.println("accept");
if("select".equalsIgnoreCase(method.getName())) {
return 0;
} else if ("delete".equalsIgnoreCase(method.getName())) {
return 1;
}
return 2;
}
}
接口和实现类:
public interface IDao {
public void select();
public void insert();
}
public class Dao implements IDao{
@Override
public void select() {
System.out.println("select 1 from dual:");
insert();
}
@Override
public void insert() {
System.out.println("insert into ...");
}
}
主程序:
public class Client {
public static void main(String[] args) {
//将代理类存到本地磁盘
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./");
//实例化增强器
Enhancer enhancer = new Enhancer();
//设置需要代理的目标类
enhancer.setSuperclass(Dao.class);
//设置拦截对象 回调的实现类
enhancer.setCallback(new DaoProxy());
//使用create 返回Object 生成代理类并返回实例
Dao dao = (Dao) enhancer.create();
//select优先级高 使用DaoProxy
dao.select();
//无法代理被final修饰的方法
//dao.delete();
// dao.insert();
}
}
运行主程序可以看到输出:
生成的代理类class文件:
public class Dao implements IDao {
public Dao() {
}
public void select() {
DaoAnotherProxy.before();
System.out.println("select 1 from dual");
}
public void insert() {
System.out.println("insert into ...");
}
public final void delete() {
System.out.println("delete from ...");
}
}