论坛首页 入门技术论坛

ASM函数监听实现

浏览 2498 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2012-06-05   最后修改:2012-06-11
1.目的:实现对函数执行监听,在函数调用前,后得到通知。考虑用asm来实现。
2.资料:在网上看到关于asm的技术资料,写了一个简单的实现。参考链接如下:
http://www.cnblogs.com/eafy/archive/2008/06/18/1224633.html
http://alvinqq.iteye.com/blog/940965
http://www.ibm.com/developerworks/cn/java/j-lo-asm30/
http://ayufox.iteye.com/blog/668917
3.代码:asmAopClassAdapter 该类对目标类进行操作。
public class asmAopClassAdapter extends ClassAdapter{
	
	private String enhancedSuperName,enhancedName;
	
	private String method;
	
	private String startInfo,endInfo;
	
    public asmAopClassAdapter(ClassVisitor cv,String methodName,String start,String end) {
        //Responsechain 的下一个 ClassVisitor,这里我们将传入 ClassWriter,
        // 负责改写后代码的输出
        super(cv); 
        method = methodName;
        startInfo = start;
        endInfo = end;
    } 
    
    // 重写 visitMethod,访问到 "method" 方法时,
    // 给出自定义 MethodVisitor,实际改写方法内容
    public MethodVisitor visitMethod(final int access, final String name, 
    		 final String desc, final String signature, final String[] exceptions) { 
   	 MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions); 
   	 MethodVisitor wrappedMv = mv; 
   	 if (mv != null) { 
   		 if (name.equals(method)) { 
   			 wrappedMv = new asmAopMethodAdapter(mv,startInfo,endInfo); 
   		 } else if (name.equals("<init>")) { 
			 wrappedMv = new ChangeToChildConstructorMethodAdapter(mv, 
					 enhancedSuperName); 
			 } 
   	 } 
   	 return wrappedMv; 
    } 
    
    public void visit(final int version, final int access, final String name, 
   		 final String signature, final String superName, 
   		 final String[] interfaces) { 
   	 enhancedName = name.replace("/", "$")+"$EnhancedByASM";  // 改变类命名
   	 enhancedSuperName = name; // 改变父类
   	 super.visit(version, access, enhancedName, signature, 
   	 enhancedSuperName, interfaces); 
    } 
    
	public String getEnhancedName() {
		return enhancedName;
	}
}

asmAopMethodAdapter 对目标类的方法做操作。
public class asmAopMethodAdapter extends MethodAdapter implements Opcodes{
	
	//private final static int EXCEPTION_STACK = 2 + 1;//max_stack至少需要能够容纳2个常量地址(监控方法使用)和1个exception地址
	
	private Label try_catch_start,try_catch_end;
	
	private String startInfo,endInfo;
	
	 public asmAopMethodAdapter(MethodVisitor mv,String start,String end) { 
		 super(mv); 
		 try_catch_start = new Label();
		 try_catch_end = new Label();
		 startInfo = start;
		 endInfo = end;
	 } 

	 public void visitCode() {
		 mv.visitCode();
		 mv.visitLabel(try_catch_start);
		 
		 mv.visitLdcInsn(startInfo);
//asmAopInvoker 这里写类的路径例如:com.asm.asmAopInvoker 应写成 com/asm/asmAopInvoker  
		 mv.visitMethodInsn(INVOKESTATIC, "asmAopInvoker", 
					"methodStart", "(Ljava/lang/String;)V");
	 } 
	 
	 public void visitInsn(int opcode){
		 if(opcode >= IRETURN && opcode <= RETURN){
			 mv.visitLdcInsn(endInfo);
//asmAopInvoker 这里写类的路径例如:com.asm.asmAopInvoker 应写成 com/asm/asmAopInvoker 
			 mv.visitMethodInsn(INVOKESTATIC, "asmAopInvoker", 
						"methodEnd", "(Ljava/lang/String;)V");
		 }
		 mv.visitInsn(opcode);
	 }
	 public void visitEnd() {
		 mv.visitLabel(try_catch_end);
		 mv.visitTryCatchBlock(try_catch_start, try_catch_end, try_catch_end, null);
		 mv.visitLdcInsn(endInfo);
		 mv.visitMethodInsn(INVOKESTATIC, "asmAopInvoker", 
				 "methodEnd", "(Ljava/lang/String;)V");
		 mv.visitInsn(Opcodes.ATHROW);
		 mv.visitEnd();
	 }
	 
	 //public void visitMaxs(int maxStack,int maxLocals){
		//保证max stack足够大
	//	 mv.visitMaxs(Math.max(EXCEPTION_STACK,maxStack), maxLocals);
	 //}
}

ChangeToChildConstructorMethodAdapter该类生成目标类的子类。
public class ChangeToChildConstructorMethodAdapter extends MethodAdapter { 
	 
	private String superClassName; 

	 public ChangeToChildConstructorMethodAdapter(MethodVisitor mv, 
		 String superClassName) { 
		 super(mv); 
		 this.superClassName = superClassName; 
	 } 

	 public void visitMethodInsn(int opcode, String owner, String name, 
		 String desc) { 
		 // 调用父类的构造函数时
		 if (opcode == Opcodes.INVOKESPECIAL && name.equals("<init>")) { 
			 owner = superClassName; 
		 } 
		 super.visitMethodInsn(opcode, owner, name, desc);// 改写父类为 superClassName 
	 } 
} 

asmAopGenerator 一个工具类,生成代理目标类的对象。
public class asmAopGenerator {
	
	private AOPGeneratorClassLoader classLoader ;
	
	public asmAopGenerator(){
		classLoader = new AOPGeneratorClassLoader();
	}
	
	
	public Object proxy(Class c,String methodName,String startInfo,String endInfo) {
		try{
		if( c != null){
		String classPach = c.toString().replace("/", ".");
			 ClassReader cr = new ClassReader(classPach.substring(6,classPach.length())); 
			 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); 
			 asmAopClassAdapter classAdapter = new asmAopClassAdapter(cw,methodName,startInfo,endInfo); 
			 cr.accept(classAdapter, ClassReader.SKIP_DEBUG); 
			 byte[] data = cw.toByteArray();
			 Class obj = classLoader.defineClassFromClassFile(classAdapter.getEnhancedName(), data);
			 //TODO:隐藏BUG
			 return obj.newInstance();
		}}catch(Exception e){
			e.printStackTrace();
		}
		return null;
	}
	
	
	 class AOPGeneratorClassLoader extends ClassLoader {
	        public Class defineClassFromClassFile(String className, 
	            byte[] classFile) throws ClassFormatError { 
	            return defineClass(className, classFile, 0, 
		        classFile.length);
	        } 
	    } 
}


asmAopInvoker 函数注入类
public class asmAopInvoker {
	
	public static void methodEnd(String evtID){
		System.out.println(evtID);
	}
	
	public static void methodStart(String evtID){
		System.out.println(evtID);
	}
	
}


测试类
public class helloWorld {
	
	public void sayHello(){
		System.out.println("helloWorld....");
	}
	
	public static void main(String[]args){
		asmAopGenerator aag = new asmAopGenerator();
		helloWorld hw = (helloWorld) aag.proxy(helloWorld.class, "sayHello", "it's begin", "it's end");
		hw.sayHello();
	}
}

输出结果
it's begin
helloWorld....
it's end

4.目的基本达成。
5.待改进,在监听函数执行后,是用的try和catch抛异常的方式来实现。
   发表时间:2012-06-08  
类名首字母大写啊。
另外既然用来ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS)
那么
//保证max stack足够大 
mv.visitMaxs(Math.max(EXCEPTION_STACK,maxStack), maxLocals);
这里的maxstack 和 maxlocals 的值就不起作用了。 asm会自动计算的。
0 请登录后投票
   发表时间:2012-06-11  
wensiqun 写道
类名首字母大写啊。
另外既然用来ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS)
那么
//保证max stack足够大 
mv.visitMaxs(Math.max(EXCEPTION_STACK,maxStack), maxLocals);
这里的maxstack 和 maxlocals 的值就不起作用了。 asm会自动计算的。

已将visitMaxs,方法屏蔽。多谢指正。
0 请登录后投票
论坛首页 入门技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics