ASM函数监听实现-之简单静态方法注入

来源:互联网 发布:matlab 如何遍历数组 编辑:程序博客网 时间:2024/05/16 17:37
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 该类对目标类进行操作。 
Java代码  收藏代码
  1. public class asmAopClassAdapter extends ClassAdapter{  
  2.       
  3.     private String enhancedSuperName,enhancedName;  
  4.       
  5.     private String method;  
  6.       
  7.     private String startInfo,endInfo;  
  8.       
  9.     public asmAopClassAdapter(ClassVisitor cv,String methodName,String start,String end) {  
  10.         //Responsechain 的下一个 ClassVisitor,这里我们将传入 ClassWriter,  
  11.         // 负责改写后代码的输出  
  12.         super(cv);   
  13.         method = methodName;  
  14.         startInfo = start;  
  15.         endInfo = end;  
  16.     }   
  17.       
  18.     // 重写 visitMethod,访问到 "method" 方法时,  
  19.     // 给出自定义 MethodVisitor,实际改写方法内容  
  20.     public MethodVisitor visitMethod(final int access, final String name,   
  21.              final String desc, final String signature, final String[] exceptions) {   
  22.      MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);   
  23.      MethodVisitor wrappedMv = mv;   
  24.      if (mv != null) {   
  25.          if (name.equals(method)) {   
  26.              wrappedMv = new asmAopMethodAdapter(mv,startInfo,endInfo);   
  27.          } else if (name.equals("<init>")) {   
  28.              wrappedMv = new ChangeToChildConstructorMethodAdapter(mv,   
  29.                      enhancedSuperName);   
  30.              }   
  31.      }   
  32.      return wrappedMv;   
  33.     }   
  34.       
  35.     public void visit(final int version, final int access, final String name,   
  36.          final String signature, final String superName,   
  37.          final String[] interfaces) {   
  38.      enhancedName = name.replace("/""$")+"$EnhancedByASM";  // 改变类命名  
  39.      enhancedSuperName = name; // 改变父类  
  40.      super.visit(version, access, enhancedName, signature,   
  41.      enhancedSuperName, interfaces);   
  42.     }   
  43.       
  44.     public String getEnhancedName() {  
  45.         return enhancedName;  
  46.     }  
  47. }  

asmAopMethodAdapter 对目标类的方法做操作。 
Java代码  收藏代码
  1. public class asmAopMethodAdapter extends MethodAdapter implements Opcodes{  
  2.       
  3.     private final static int EXCEPTION_STACK = 2 + 1;//max_stack至少需要能够容纳2个常量地址(监控方法使用)和1个exception地址  
  4.       
  5.     private Label try_catch_start,try_catch_end;  
  6.       
  7.     private String startInfo,endInfo;  
  8.       
  9.      public asmAopMethodAdapter(MethodVisitor mv,String start,String end) {   
  10.          super(mv);   
  11.          try_catch_start = new Label();  
  12.          try_catch_end = new Label();  
  13.          startInfo = start;  
  14.          endInfo = end;  
  15.      }   
  16.   
  17.      public void visitCode() {  
  18.          mv.visitCode();  
  19.          mv.visitLabel(try_catch_start);  
  20.            
  21.          mv.visitLdcInsn(startInfo);  
  22. //asmAopInvoker 这里写类的路径例如:com.asm.asmAopInvoker 应写成 com/asm/asmAopInvoker    
  23.          mv.visitMethodInsn(INVOKESTATIC, "asmAopInvoker",   
  24.                     "methodStart""(Ljava/lang/String;)V");  
  25.      }   
  26.        
  27.      public void visitInsn(int opcode){  
  28.          if(opcode >= IRETURN && opcode <= RETURN){  
  29.              mv.visitLdcInsn(endInfo);  
  30. //asmAopInvoker 这里写类的路径例如:com.asm.asmAopInvoker 应写成 com/asm/asmAopInvoker   
  31.              mv.visitMethodInsn(INVOKESTATIC, "asmAopInvoker",   
  32.                         "methodEnd""(Ljava/lang/String;)V");  
  33.          }  
  34.          mv.visitInsn(opcode);  
  35.      }  
  36.      public void visitEnd() {  
  37.          mv.visitLabel(try_catch_end);  
  38.          mv.visitTryCatchBlock(try_catch_start, try_catch_end, try_catch_end, null);  
  39.          mv.visitLdcInsn(endInfo);  
  40. //asmAopInvoker 这里写类的路径例如:com.asm.asmAopInvoker 应写成 com/asm/asmAopInvoker   
  41.          mv.visitMethodInsn(INVOKESTATIC, "asmAopInvoker",   
  42.                  "methodEnd""(Ljava/lang/String;)V");  
  43.          mv.visitInsn(Opcodes.ATHROW);  
  44.          mv.visitEnd();  
  45.      }  
  46.        
  47.      public void visitMaxs(int maxStack,int maxLocals){  
  48.         //保证max stack足够大  
  49.          mv.visitMaxs(Math.max(EXCEPTION_STACK,maxStack), maxLocals);  
  50.      }  
  51. }  

ChangeToChildConstructorMethodAdapter该类生成目标类的子类。 
Java代码  收藏代码
  1. public class ChangeToChildConstructorMethodAdapter extends MethodAdapter {   
  2.        
  3.     private String superClassName;   
  4.   
  5.      public ChangeToChildConstructorMethodAdapter(MethodVisitor mv,   
  6.          String superClassName) {   
  7.          super(mv);   
  8.          this.superClassName = superClassName;   
  9.      }   
  10.   
  11.      public void visitMethodInsn(int opcode, String owner, String name,   
  12.          String desc) {   
  13.          // 调用父类的构造函数时  
  14.          if (opcode == Opcodes.INVOKESPECIAL && name.equals("<init>")) {   
  15.              owner = superClassName;   
  16.          }   
  17.          super.visitMethodInsn(opcode, owner, name, desc);// 改写父类为 superClassName   
  18.      }   
  19. }   

asmAopGenerator 一个工具类,生成代理目标类的对象。 
Java代码  收藏代码
  1. public class asmAopGenerator {  
  2.       
  3.     private AOPGeneratorClassLoader classLoader ;  
  4.       
  5.     public asmAopGenerator(){  
  6.         classLoader = new AOPGeneratorClassLoader();  
  7.     }  
  8.       
  9.       
  10.     public Object proxy(Class c,String methodName,String startInfo,String endInfo) {  
  11.         try{  
  12.         if( c != null){  
  13.         String classPach = c.toString().replace("/"".");  
  14.              ClassReader cr = new ClassReader(classPach.substring(6,classPach.length()));   
  15.              ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);   
  16.              asmAopClassAdapter classAdapter = new asmAopClassAdapter(cw,methodName,startInfo,endInfo);   
  17.              cr.accept(classAdapter, ClassReader.SKIP_DEBUG);   
  18.              byte[] data = cw.toByteArray();  
  19.              Class obj = classLoader.defineClassFromClassFile(classAdapter.getEnhancedName(), data);  
  20.              //TODO:隐藏BUG  
  21.              return obj.newInstance();  
  22.         }}catch(Exception e){  
  23.             e.printStackTrace();  
  24.         }  
  25.         return null;  
  26.     }  
  27.       
  28.       
  29.      class AOPGeneratorClassLoader extends ClassLoader {  
  30.             public Class defineClassFromClassFile(String className,   
  31.                 byte[] classFile) throws ClassFormatError {   
  32.                 return defineClass(className, classFile, 0,   
  33.                 classFile.length);  
  34.             }   
  35.         }   
  36. }  


asmAopInvoker 函数注入类 
Java代码  收藏代码
  1. public class asmAopInvoker {  
  2.       
  3.     public static void methodEnd(String evtID){  
  4.         System.out.println(evtID);  
  5.     }  
  6.       
  7.     public static void methodStart(String evtID){  
  8.         System.out.println(evtID);  
  9.     }  
  10.       
  11. }  

测试类 
Java代码  收藏代码
  1. public class helloWorld {  
  2.       
  3.     public void sayHello(){  
  4.         System.out.println("helloWorld....");  
  5.     }  
  6.       
  7.     public static void main(String[]args){  
  8.         asmAopGenerator aag = new asmAopGenerator();  
  9.         helloWorld hw = (helloWorld) aag.proxy(helloWorld.class"sayHello""it's begin""it's end");  
  10.         hw.sayHello();  
  11.     }  
  12. }  

输出结果 
Java代码  收藏代码
  1. it's begin  
  2. helloWorld....  
  3. it's end  

4.目的基本达成。 
5.待改进,在监听函数执行后,是用的try和catch抛异常的方式来实现。 

6.环境 asm 3.3.1 jdk 1.6.


转载:http://xkorey.iteye.com/blog/1551897

原创粉丝点击