ASM实践

ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。

其官网:http://asm.ow2.org/。可以下载到最新版的ASM,同时也有一个user guid。

下面介绍ASM的使用。(注意:对于包名,我在贴代码的时候,没有处理,需要自己根据自己的包名修改处理)。

关于java字节码指令以及ASM的一些理论,请看ASM-java字节码控制框架

生成字节码:

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;

/**
 * 生成一个如下的类:=================================================================
 * public interface com.asmx.geners.Comparable extends com.asmx.geners.Mesurable
 * {public static final int LESS;
 * 
 * public static final int EQUAL;
 * 
 * public static final int GREATER;
 * 
 * public abstract int compareTo(java.lang.Object); }
 * 
 * @author MiXian
 *
 */
public class HelloWorld extends ClassLoader implements Opcodes {

	public static void main(String[] args) throws IOException {
		// 生成一个类只需要ClassWriter组件即可,0,COMPUTE_MAXS(1),COMPUTE_FRAMES(2)
		ClassWriter cw = new ClassWriter(1);
		// 通过visit方法确定类的头部信息
		// visit{版本,访问性,名称,签名(不是泛型的时候,用null),父类,接口}
		cw.visit(Opcodes.V1_7, Opcodes.ACC_PUBLIC, "CompareResult", null, "java/lang/Object", null);
		// 定义类的属性
		// visitField{访问性,名称,类型,签名(不是泛型的时候,用null),值}
		cw.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "LESS", "I", null, new Integer(-1)).visitEnd();
		cw.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, "EQUAL", "I", null, new Integer(0))
				.visitEnd();
		cw.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, "GREATER", "I", null, new Integer(1))
				.visitEnd();
		// 定义类的方法
		// visitMethod{访问性,名称,类型,签名(不是泛型的时候,用null),异常}
		// cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_ABSTRACT,
		// "compareTo", "(Ljava/lang/Object;)I", null, null)
		// .visitEnd();
		cw.visitEnd(); // 使cw类已经完成
		// 将cw转换成字节数组写到文件里面去
		byte[] data = cw.toByteArray();
		FileOutputStream fout = null;
		try {
			File file = new File("D://CompareResult.class");
			fout = new FileOutputStream(file);
			fout.write(data);
		} finally {
			if (fout != null) {
				fout.close();
			}
		}
	}
}

修改字节码:

根据方法插入字节码:

原来的类:

public class OriginalMe {
	private int i;
	void fun() throws InterruptedException {
		Thread.sleep(100);
	}
}

准备生成的类:

public class OriginalMe {
    public static long timer;
    private int i;
    public void fun() throws InterruptedException{
        timer -= System.currentTimeMillis();
        Thread.sleep(100); 
        timer += System.currentTimeMillis();
    }
}


字节码操作:

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

/**
 * 为类增加一个static long time域。用来记录这个类执行方法用时。 这里,没有对多线程做处理。
 */
public class AddTimeClassVisitor extends ClassVisitor {

	private boolean isInterface;

	private String clazz;

	public AddTimeClassVisitor(ClassWriter cw) {
		super(Opcodes.ASM5, cw);
	}

	@Override
	public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
		// 这里处理了包名,一定注意,类的全名包括包名,要符合java规范。包名的根就是source
		cv.visit(version, access, name.substring(name.lastIndexOf("/") + 1), signature, superName, interfaces);
		// cv.visit(version, access, name, signature, superName, interfaces);
		isInterface = (access & Opcodes.ACC_INTERFACE) != 0;
		clazz = name;
		if (!isInterface) {
			// 添加字段
			cv.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "timer", "J", null, null);
		}
	}

	// 配套的visitMethod方法
	// 要这个字段发挥作用,就要在class访问方法的时候,加入时间统计,所以需要修改其方法。
	@Override
	public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
		MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
		if (!"<clinit>".equals(name) && !name.equals("<init>") && !isInterface && mv != null) {
			mv = new AddTimeHelperClassVisitor(clazz, mv);
		}
		return mv;
	}

	// 要这个字段发挥作用,就要在class访问方法的时候,加入时间统计,所以需要修改其方法。
	private static class AddTimeHelperClassVisitor extends MethodVisitor {
		private String clazz;

		public AddTimeHelperClassVisitor(String name, MethodVisitor mv) {
			super(Opcodes.ASM5, mv);
			this.clazz = name;
		}

		// 访问方法,非抽象方法,就是具体实现的代码。即方法刚刚开始访问的时候。
		@Override
		public void visitCode() {
			mv.visitCode();
			// 访问域指令{域的操作码,域所属的类,域名称,类型}
			mv.visitFieldInsn(Opcodes.GETSTATIC, clazz, "timer", "J");
			// 最后一个参数表示这个方法是不是接口
			mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
			mv.visitInsn(Opcodes.LSUB);
			mv.visitFieldInsn(Opcodes.PUTSTATIC, clazz, "timer", "J");
		}

		// 操作指令地方,检查每条指令,对这些指令进行修改。
		@Override
		public void visitInsn(int opcode) {
			if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) || opcode == Opcodes.ATHROW) {
				mv.visitFieldInsn(Opcodes.GETSTATIC, clazz, "timer", "J");
				mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
				mv.visitInsn(Opcodes.LADD);
				mv.visitFieldInsn(Opcodes.PUTSTATIC, clazz, "timer", "J");
			}
			mv.visitInsn(opcode);
		}

	}

}

调用方法:

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;

public class AddRun {
	public static void main(String[] args) throws IOException {
		FileOutputStream fout = null;
		try {
			ClassReader cr = new ClassReader("advanced/asms/modifies/OriginalMe");
			ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
			ClassVisitor visitor = new AddTimeClassVisitor(cw);
			// 使给定的访问者访问Java类的ClassReader
			cr.accept(visitor, ClassReader.SKIP_DEBUG);
			byte[] data = cw.toByteArray();
			File file = new File("D:\\OriginalMe.class");
			fout = new FileOutputStream(file);
			fout.write(data);
			System.out.println("success!");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (fout != null) {
				fout.close();
			}
		}
	}
}


根据指令插入字节码:

原来的类:

public class OriginField {
	private static int count = 0;

	static {
		another();
	}

	private static void another() {
		++count;
	}

	private String str;
	private int inT;
	private char[] charr;
	private Object obj;

	public String getStr() {
		return str;
	}

	public void setStr(String str) {
		this.str = str;
	}

	public int getInT() {
		return inT;
	}

	public void setInT(int inT) {
		this.inT = inT;
	}

	public char getCharr(int i) {
		return charr[i];
	}

	public void setCharr(int i, char c) {
		charr[i] = c;
	}

	public Object getObj() {
		return obj;
	}

	public void setObj(Object obj) {
		this.obj = obj;
	}

	public static int getCount() {
		return count;
	}

	public OriginField(String str, int inT, char[] charr, Object obj) {
		super();
		this.str = str;
		this.inT = inT;
		this.charr = charr;
		this.obj = obj;
	}
}

使用的工具:

 

package advanced.asms.modifies.fields;

public class ActionUtils {

	public static void fieldChange(Object instance, String field, Object oldVaule, Object newValue) {
		System.out.println("object" + instance + "|field:" + field + "{old:" + oldVaule + ";new:" + newValue + "}");
	}

	public static void arrayChange(Object instance, int i, Object value) {
		System.out.println("object" + instance + ":array[index" + i + "::{new:" + value + "}]");

	}
}

新生成的类:

public class OriginField {
	private static int count;
	private String str;
	private int inT;
	private char[] charr;
	private Object obj;

	static {
		advanced.asms.modifies.fields.OriginField.count = 0;
		advanced.asms.modifies.fields.OriginField.another();
	}

	private static void another() {
		int i = advanced.asms.modifies.fields.OriginField.count;
		advanced.asms.modifies.fields.OriginField.count += 1;
		Object localObject;
		advanced.asms.modifies.fields.ActionUtils.fieldChange(localObject, "count", i,
				advanced.asms.modifies.fields.OriginField.count);
	}

	public String getStr() {
		return this.str;
	}

	public void setStr(String paramString) {
		String str1 = this.str;
		this.str = paramString;
		advanced.asms.modifies.fields.ActionUtils.fieldChange(this, "str", str1, paramString);
	}

	public int getInT() {
		return this.inT;
	}

	public void setInT(int paramInt) {
		int i = this.inT;
		this.inT = paramInt;
		advanced.asms.modifies.fields.ActionUtils.fieldChange(this, "inT", i, paramInt);
	}

	public char getCharr(int paramInt) {
		return this.charr[paramInt];
	}

	public void setCharr(int paramInt, char paramChar) {
		this.charr[paramInt] = paramChar;
		advanced.asms.modifies.fields.ActionUtils.arrayChange(this, paramInt, paramChar);
	}

	public Object getObj() {
		return this.obj;
	}

	public void setObj(Object paramObject) {
		Object localObject = this.obj;
		this.obj = paramObject;
		advanced.asms.modifies.fields.ActionUtils.fieldChange(this, "obj", localObject, paramObject);
	}

	public static int getCount() {
		return advanced.asms.modifies.fields.OriginField.count;
	}

	public OriginField(String paramString, int paramInt, char[] paramArrayOfChar, Object paramObject) {
		this.str = paramString;
		this.inT = paramInt;
		this.charr = paramArrayOfChar;
		this.obj = paramObject;
	}
}

字节码修改:

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

/**
 * 就是检测到域的变化的指令的时候,采取行动。
 */
public class FieldChangeSimpleClassVisitor extends ClassVisitor implements Opcodes {

	private static final String METHODOWNER = "advanced.asms.modifies.fields.ActionUtils";
	private static final String FIELDCHANGEDESC = "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V";
	private static final String FIELDCHANGE = "fieldChange";
	private static final String ARRAYCHANGE = "arrayChange";
	private static final String ARRAYCHANGEDESC = "(Ljava/lang/Object;ILjava/lang/Object;)V";

	public FieldChangeSimpleClassVisitor(ClassWriter cw) {
		super(Opcodes.ASM5, cw);
	}

	@Override
	public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
		cv.visit(version, access, name.substring(name.lastIndexOf("/") + 1), signature, superName, interfaces);
	}

	@Override
	public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
		MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
		if ("<clinit>".equals(name) || "<init>".equals(name)) {
			return mv;
		}
		return new Helper(api, mv);
	};

	private class Helper extends MethodVisitor implements Opcodes {

		public Helper(int api, MethodVisitor mv) {
			super(api, mv);
		}

		/**
		 * 数组变动的时候。
		 */
		@Override
		public void visitInsn(int opCode) {
			mv.visitInsn(opCode);
			if (isArrayOperatorInstrumentationReq(opCode)) {
				mv.visitVarInsn(ALOAD, 0);
				mv.visitVarInsn(ILOAD, 1);
				mv.visitVarInsn(ILOAD, 2);
				mv.visitMethodInsn(INVOKESTATIC, METHODOWNER, ARRAYCHANGE, ARRAYCHANGEDESC, false);
			}
		}

		/**
		 * 域变动的指令
		 */
		@Override
		public void visitFieldInsn(int opcode, String owner, String name, String desc) {

			// 指令处理,先取出旧值,再写入新值,否则会影响原来指令的处理流程
			// 插入指令,这里把旧值保存下来
			if (opcode == PUTFIELD || opcode == PUTSTATIC) {
				storeOldFieldValue(opcode == PUTSTATIC, owner, name, desc);
			}
			// 实际处理,这里是原来的指令
			mv.visitFieldInsn(opcode, owner, name, desc);
			// 将新值和旧值一起调用处理
			if (opcode == PUTFIELD) {
				triggerFieldAction(owner, name, desc, false);
			} else if (opcode == PUTSTATIC) {
				triggerFieldAction(owner, name, desc, true);
			}
		}

		private void triggerFieldAction(String owner, String name, String desc, boolean isStatic) {
			mv.visitVarInsn(ALOAD, 0);
			mv.visitLdcInsn(name);
			mv.visitVarInsn(ALOAD, 2);
			if (isStatic) {
				mv.visitFieldInsn(GETSTATIC, owner, name, desc);
			} else {
				mv.visitVarInsn(ALOAD, 1);
			}
			mv.visitMethodInsn(INVOKESTATIC, METHODOWNER, FIELDCHANGE, FIELDCHANGEDESC, false);
		}

		/**
		 * 旧值存入栈
		 */
		private void storeOldFieldValue(boolean isStatic, String owner, String name, String desc) {
			if (isStatic) {
				// 旧值存入栈
				mv.visitFieldInsn(GETSTATIC, owner, name, desc);
				mv.visitVarInsn(ASTORE, 2);
			} else {
				// 旧值存入栈
				mv.visitVarInsn(ALOAD, 0);
				mv.visitFieldInsn(GETFIELD, owner, name, desc);
				mv.visitVarInsn(ASTORE, 2);
			}
		}

		// 数组的变动的指令们
		private boolean isArrayOperatorInstrumentationReq(int opCode) {
			return opCode == AALOAD || opCode == AASTORE || opCode == LASTORE || opCode == SASTORE || opCode == IASTORE
					|| opCode == DASTORE || opCode == FASTORE || opCode == BASTORE || opCode == CASTORE;
		}
	}

}

调用:

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;

public class Main {
	public static void main(String[] args) throws IOException {
		FileOutputStream fout = null;
		try {
			ClassReader cr = new ClassReader("advanced/asms/modifies/fields/OriginField");
			ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
			ClassVisitor visitor = new FieldChangeSimpleClassVisitor(cw);
			// 使给定的访问者访问Java类的ClassReader
			cr.accept(visitor, ClassReader.SKIP_DEBUG);
			byte[] data = cw.toByteArray();
			File file = new File("D:\\OriginField.class");
			fout = new FileOutputStream(file);
			fout.write(data);
			System.out.println("success!");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (fout != null) {
				fout.close();
			}
		}
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值