Java中ASM框架详解

什么是asm呢?asm是assembly的缩写,是汇编的称号,对于java而言,asm就是字节码级别的编程。  
而这里说到的asm是指objectweb asm,一种.class的代码生成器的开源项目.  
ASM是一套java字节码生成架构,它可以动态生成二进制格式的stub类或其它代理类,  
或者在类被java虚拟机装入内存之前,动态修改类。  
现在挺多流行的框架都使用到了asm.所以从aop追溯来到了这。  
 
1.什么是ObjectWeb ASM  
    ObjectWeb ASM是轻量级的Java字节码处理框架。它可以动态生成二进制格式的stub类或其他代理类,或者在类被JAVA虚拟机装入内存之前,动态修改类。   
    ASM 提供了与 BCEL和SERP相似的功能,只有22K的大小,比起350K的BCEL和150K的SERP来说,是相当小巧的,并且它有更高的执行效率,  
    是BCEL 的7倍,SERP的11倍以上。  
 
在我看来,ObjectWeb ASM具有如下几个非常诱人的特点  
    * 小巧、高效  
    * 源代码实现非常简洁而又优雅,简直就是Gof的《设计模式》非常棒的注解  
    * 字节码级的控制,能够更高效地实现字节码的控制  
 
ObjectWeb ASM有2组接口:  
    * 基于事件驱动的接口,类似于xml的SAX接口,visitor模式,在访问到类定义某个部分的时候进行回调,实现上比tree接口高效,占用内存更小  
    * 基于tree的接口,类似于xml的DOM接口,将类定义解析成tree  
 
这里我们将使用ObjectWeb ASM的事件驱动接口  
 
2. 目标  
    我们将对已有的字节码进行增强,收集进入方法和退出方法的信息,这里主要解决Method Monitor的字节码增强部分,  
    不对收集后的数据处理做更深入地研究,出于演示的目的,我们定义了如下的收集方法的访问信息处理,  
    在实际应用中,我们可能会使用更好的格式收集更多的数据、使用异步处理提高性能、使用批量处理提高处理能力、使用友好的UI显示信息等等,  
    此处不对这部分进行探讨  
 
   1. package blackstar.methodmonitor.instrutment.monitor;    
   2. public class MonitorUtil    
   3. {    
   4.     public final static String CLASS_NAME = MonitorUtil.class.getName()    
   5.             .replaceAll("\\.", "/");    
   6.     public final static String ENTRY_METHOD = "entryMethod";    
   7.     public final static String EXIT_METHOD = "exitMethod";    
   8.     public final static String METHOD = "(Ljava/lang/String;Ljava/lang/String;)V";    
   9.     
  10.     public static void entryMethod(String className, String methodName)    
  11.     {    
  12.         System.out.println("entry : " + className + "." + methodName);    
  13.     }    
  14.     
  15.     public static void exitMethod(String className, String methodName)    
  16.     {    
  17.         System.out.println("exit : " + className + "." + methodName);    
  18.     }    
  19. }    
 
3. 从字节码开始  
实际上,对于被监控制的代码,我们所需要实现的功能如下,红色部分的代码是我们需要在动态期插到字节码中间的  
 
public xxx method(…)  
{  
    try  
    {  
         methodEntry(…)  
 
         methodCode  
     }  
      finally  
     {  
          methodExit(…)  
     }  
}   
 
这个问题看起来简单,实际则没有那么容易,因为在JVM的字节码设计中,字节码并不直接支持finally语句,而是使用try…catch来模拟的,我们先来看一个例子  
 
Java代码  
 
   1. package blackstar.methodmonitor.instrutment.test;    
   2.     
   3. public class Test    
   4. {    
   5.     public void sayHello() throws Exception    
   6.     {    
   7.         try    
   8.         {    
   9.             System.out.println("hi");    
  10.         } catch (Exception e)    
  11.         {    
  12.             System.out.println("exception");    
  13.             return;    
  14.         } finally    
  15.         {    
  16.             System.out.println("finally");    
  17.         }    
  18.     }    
  19. }    
 
我们看看字节码是如何处理finally语句的  
      首先看看异常表,异常是在JVM级别上直接支持的,下面异常表的意思是,在执行0-8语句的时候,如果有异常java.lang.Exception抛出,则进入第11语句,  
    在执行0-20语句的时候,有任何异常抛出,都进入29语句。实际上JVM是这样实现finally语句的:  
 
    * 在任何return语句之前,都会增加finally语句中的字节码  
    * 定义一个捕获所有异常的语句,增加finally语句中的字节码,如果finally中没有return语句,则会将异常再次抛出去(处理方法以抛出异常的方式结束)  
 
Exceptions:  
[0-8): 11 - java.lang.Exception  
[0-20): 29   
 
我们再看看字节码具体是如何做的  
 
0 getstatic java.lang.System.out  
3 ldc "hi" (java.lang.String)  
5 invokevirtual println  
8 goto 40  
// System.out.println("hi");,执行完之后执行返回(goto 40)  
11 astore_1  
12 getstatic java.lang.System.out  
15 ldc "exception" (java.lang.String)  
17 invokevirtual println  
// System.out.println("exception");  
20 getstatic java.lang.System.out  
23 ldc "finally" (java.lang.String)  
25 invokevirtual println  
// return语句之前插入finally部分字节码  
// System.out.println("finally");  
28 return  
29 astore_2  
30 getstatic java.lang.System.out  
33 ldc "finally" (java.lang.String)  
35 invokevirtual println  
38 aload_2  
39 athrow  
//当在执行0-29语句中,如果有异常抛出,则执行这段finally语句  
//此处的astore_2(将栈顶值——即exception的地址——设给第2个local变量)和aload_2(将第2个local变量的值入栈)这两个字节码实际是不必要的,  
//但需要注意的是,如果这2段代码去掉的话,要考虑增大操作栈(max stack)以容纳这个exception地址  
//System.out.println("finally");  
40 getstatic java.lang.System.out  
43 ldc "finally" (java.lang.String)  
45 invokevirtual println  
// return语句之前插入finally部分字节码  
// System.out.println("finally");  
48 return   
 
实际上,我们需要做的就是  
    * 在方法进入时插入方法进入代码(需要注意,对于构造函数不允许做这种处理,构造函数第一步必须调用父类的构造函数。  
    * 在每个return操作(包括return、ireturn、freturn等)之前,插入方法退出代码  
    * 定义一个捕获所有异常的处理,在处理中,插入方法退出代码(即方法以抛异常的方式终止执行)  
 
4. 实现  
      我们看看使用ObjectWeb ASM如何实现我们上面描述的功能  
      1)ObjectWeb ASM的字节码修改  
 
   1. ClassReader cr = new ClassReader(byteArray); //使用字节码构监一个reader    
   2. ClassWriter cw = new ClassWriter(cr, 0);//writer将基于已有的字节码进行修改    
   3. MonitorClassVisitor ca = new MonitorClassVisitor(cw);//修改处理回调类    
   4. cr.accept(ca, 0);

时间: 2024-08-27 18:43:57

Java中ASM框架详解的相关文章

java中static作用详解

static表示“全局”或者“静态”的意思,用来修饰成员变量和成员方法,也可以形成静态static代码块,但是Java语言中没有全局变量的概念. 被static修饰的成员变量和成员方法独立于该类的任何对象.也就是说,它不依赖类特定的实例,被类的所有实例共享. 只要这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区内定找到他们.因此,static对象可以在它的任何对象创建之前访问,无需引用任何对象. 用public修饰的static成员变量和成员方法本质是全局变量和全局方法,当声明它类

Java中的HashTable详解

Hashtables提供了一个很有用的方法可以使应用程序的性能达到最佳. Hashtables(哈 希表)在计算机领域中已不 是一个新概念了.它们是用来加快计算机的处理速度的,用当今的标准来处理,速度非常慢,而它们可以让你在查询许多数据条目时,很快地找到一个特殊的条目. 尽管现代的机器速度已快了几千倍,但是为了得到应用程序的最佳性能,hashtables仍然是个很有用的方法. 设想一下,你有一个 包含约一千条记录的数据文件??比如一个小企业的客户记录还有一个程序,它把记录读到内存中进行处理.每个

Java中反射机制详解

序言 在学习java基础时,由于学的不扎实,讲的实用性不强,就觉得没用,很多重要的知识就那样一笔带过了,像这个马上要讲的反射机制一样,当时学的时候就忽略了,到后来学习的知识中,很多东西动不动就用反射,所以回过头来把这个给重新补一下,自己欠下的债,迟早是要还的. ---WH 一.什么是反射? 在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的

Java中的final详解以及用途实战

浅析Java中的final关键字 谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法.下面是本文的目录大纲: 一.final关键字的基本用法 二.深入理解final关键字 若有不正之处,请多多谅解并欢迎指正. 请尊重作者劳动成果,转载请标明原文链接: http://www.cnblogs.com/dolphin0520/p/3736238.html 一

Java中Unsafe类详解

http://www.cnblogs.com/mickole/articles/3757278.html Java不能直接访问操作系统底层,而是通过本地方法来访问.Unsafe类提供了硬件级别的原子操作,主要提供了以下功能: 1.通过Unsafe类可以分配内存,可以释放内存: 类中提供的3个本地方法allocateMemory.reallocateMemory.freeMemory分别用于分配内存,扩充内存和释放内存,与C语言中的3个方法对应. public native long alloca

一看你就懂,超详细 java 中的 ClassLoader 详解

ClassLoader翻译过来就是类加载器,普通的java开发者其实用到的不多,但对于某些框架开发者来说却非常常见.理解ClassLoader的加载机制,也有利于我们编写出更高效的代码.ClassLoader的具体作用就是将class文件加载到jvm虚拟机中去,程序就可以正确运行了.但是,jvm启动的时候,并不会一次性加载所有的class文件,而是根据需要去动态加载.想想也是的,一次性加载那么多jar包那么多class,那内存不崩溃.本文的目的也是学习ClassLoader这种加载机制. 备注:

一看你就懂,超详细java中的ClassLoader详解

本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 ClassLoader翻译过来就是类加载器,普通的java开发者其实用到的不多,但对于某些框架开发者来说却非常常见.理解ClassLoader的加载机制,也有利于我们编写出更高效的代码.ClassLoader的具体作用就是将class文件加载到jvm虚拟机中去,程序就可以正确运行了.但是,jvm启动的时候,并不会一次性加载所有的class文件,而是根据需要去动态加载.想想也是的,一次性加载那么多jar包那么多class,那内存不

Java 中的泛型详解-Java编程思想

Java中的泛型参考了C++的模板,Java的界限是Java泛型的局限. 2.简单泛型 促成泛型出现最引人注目的一个原因就是为了创造容器类. 首先看一个只能持有单个对象的类,这个类可以明确指定其持有的对象的类型 class Holder1 { private Circle a; public Holder1(Circle a) { this.a = a; } Circle get() { return a; } } 上面的类的可重用性不怎么样,无法持有其他类型的任何对象,下面通过持有Object

Java中Class对象详解

java中把生成Class对象和实例对象弄混了,更何况生成Class对象和生成 instance都有多种方式.所以只有弄清其中的原理,才可以深入理解.首先要生成Class对象,然后再生成Instance.那Class对象的生 成方式有哪些呢,以及其中是如何秘密生成的呢? Class对象的生成方式如下: 1.Class.forName("类名字符串")  (注意:类名字符串必须是全称,包名+类名) 2.类名.class 3.实例对象.getClass() 通过一段小程序,来观察一下Cla