`

代码动态生成利器ASM

阅读更多

作者:薛谷雨

作者简介

 

薛谷雨,NORDSAN(北京)信息科技开发有限公司高级JAVA研发工程师,正致力于企业级异构数据交换的服务器产品的研发,在J2EE和WEB SERVICE方面有较为丰富的开发经验,您可以通过rainight@126.com与他联系。

前言

 

代码生成器(code generator,CG),顾名思义就是生成代码的工具。有了它,你就可以从一组简单的设定或者数据库设计中获得几百、几千行代码。如果不采用这项技术的话,开发者就不得不花上几个小时或者几天的时间来手工编写这些代码。另一方面,优秀的开发工具为了提供其独特的功能或者屏蔽一些容易出错的细节,也往往采用代码生成技术为使用者提供一个程序的模板框架,其目的也是为了提高编程的效率。以上观点仅是对代码生成器的一般理解,换句话说,这似乎是一个可有可无的东西,没有它,不过是多费一些人工而已。然而,本文要介绍的这套名为ASM的JAVA工具类的功能非同小可,它可以生成JAVA字节码,也就是class文件。你可以在应用程序中根据情况动态生成各式各样的class,然后就调用,达到一种近乎上帝造物般的神奇。心动不如行动,如果你也想在自己的开发中引入这一超前的编程技术,请看此文。

小巧而神奇的ASM

 

ASM是一套JAVA字节码生成架构。它可以动态生成二进制格式的stub类或其他代理类,或者在类被JAVA虚拟机装入内存之前,动态修改类。ASM 提供了与 BCEL( http://jakarta.apache.org/bcel )和SERP( http://serp.sourceforge.net/ )相似的功能,只有22K的大小,比起350K的BCEL和150K的SERP来说,是相当小巧的,并且它有更高的执行效率,是BCEL的7倍,SERP的11倍以上。ASM一贯的设计思想就是将其应用于动态生成领域,因此小巧和快捷一直是这个产品的设计和实现的指导思想。

此产品由法国电信公司的研发工程师Eric Bruneton负责。从2002年7月ASM的第一个版本发布至今,此产品已经升级了五次,日臻完美。到目前为止,ASM最新的版本是1.3.5,你可以去 http://asm.objectweb.org/ 下载。

ASM的最终目标是创建一个生成工具,可以被用来执行对任何类的处理操作(不像一些工具,比如Javassit,它只支持预先定义的类操作,然而在许多场合这一功能是有局限性的)。

JAVA的CLASS文件格式

 

要想驾驭ASM,先要了解一下JAVA的CLASS文件格式。JAVA的CLASS文件通常是树型结构。根节点包含以下元素:

  • ConstantPool:符号表;
  • FieldInfo:类中的成员变量信息;
  • MethodInfo:类中的方法描述;
  • Attribute:可选的附加节点。

FieldInfo节点包含成员变量的名称,诸如public,private,static等的标志。ConstantValue属性用来存储静态的不变的成员变量的值。Deprecated和Synthetic被用来标记一个成员变量是不被推荐的或由编译器生成的。

MethodInfo节点包含方法的名称,参数的类型和和它的返回值,方法是公有的,私有的或静态的等标志。MethodInfo包含可选的附加属性,其中最重要的是Code属性,它包含非抽象的方法的代码。Exceptions属性包含方法将抛出的Exception的名称。Deprecated和Synthetic属性的信息同上面的FieldInfo的定义一样。

根节点的可选属性有SourceFile,InnerClasses和Deprecated。SourceFile用来存储被编译成字节码的源代码文件的原始名称;InnerClasses存储内部类的信息。由于这些属性的存在,java 的类格式是可以扩展的,也就是说可以在一个class中附加一些非标准的属性, java虚拟机会忽略这些不可识别的属性,正常的加载这个class。

ConstantPool是一个由数字或字符串常量的索引组成的队列,或由此类的树的其他节点引用的,由其他对象创建的被引用常量的索引组成的队列。这个表的目标是为了减少冗余。例如,FieldInfo节点不包含节点的名称,只包含它在这一表中的索引。同样的,GETFIELD和PUTFIELD不直接包含成员变量的名称,只包含名称的索引。

精通ASM

 

Asm架构整体都围绕着两个接口,即ClassVisitor 和 CodeVisitor,它们能访问每个类的方法,成员变量,包含在每个方法中的字节码指令。ClassReader用来读取class文件;ClassWriter类用来写生成的Class文件。

为了修改已经存在的class,你必须使用分析class文件的ClassReader,类的修正器和写class文件的ClassWriter。类的修正器就是一个ClassVisitor,它可以委派一部分工作到其他的ClassVisitor,但是为了实现预期的修改步骤,它将改变一些参数的值,或者调用一些其他方法。为了比较容易的实现这种类的修正器,ASM提供了一个ClassAdapter和CodeAdapter,这两个适配器类分别实现了ClassVistor和CodeVistor接口。

HelloWorld,体验造类的神奇

 

下面是一个应用ASM动态生成字节码的类,并调用其中方法的完整的HelloWorld 程序,程序的功能是动态生成一个Example.class类,并实例化一个Example对象,调用对象的main函数,在屏幕上打印出"Hello world!"

import org.objectweb.asm.*;
import java.lang.reflect.*;
import java.io.FileOutputStream;

public class Helloworld extends ClassLoader implements Constants {

  public static void main (final String args[]) throws Exception {

    /*
     * 此程序将生成一个class,对应的java源代码是:
     *
     * public class Example {
     *   public static void main (String[] args) {
     *     System.out.println("Hello world!");
     *   }
     * }
     *
     */

    // 创建一个ClassWriter
    ClassWriter cw = new ClassWriter(false);
    cw.visit(ACC_PUBLIC, "Example", "java/lang/Object", null, null);

    // 创建一个 MethodWriter
    CodeVisitor mw = cw.visitMethod(ACC_PUBLIC, "", "()V", null);
    // 推入 'this' 变量
    mw.visitVarInsn(ALOAD, 0);
    //  创建父类的构造函数
    mw.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V");
    mw.visitInsn(RETURN);
    // 这段代码使用最多一个栈元素和一个本地变量
    mw.visitMaxs(1, 1);

    // 为main方法创建一个MethodWriter
    mw = cw.visitMethod(
      ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null);
    // 使用System类的out成员类
    mw.visitFieldInsn(
      GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
    // pushes the "Hello World!" String constant
    mw.visitLdcInsn("Hello world!");
    // 调用System.out的'println' 函数
    mw.visitMethodInsn(
      INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
    mw.visitInsn(RETURN);
    // 这段代码使用最多两个栈元素和两个本地变量
    mw.visitMaxs(2, 2);

    // 生成字节码形式的类
    byte[] code = cw.toByteArray();

    FileOutputStream fos = new FileOutputStream("Example.class");
    //写文件
    fos.write(code);
    //关闭输出流
    fos.close();

    //实例化刚刚生成的类
    Helloworld loader = new Helloworld();
    Class exampleClass = loader.defineClass("Example", code, 0, code.length);

    // 使用动态生成的类打印 'Helloworld'
    Method main = exampleClass.getMethods()[0];
    main.invoke(null, new Object[] {null});
  }
}

 

分享到:
评论

相关推荐

    AOP 的利器 ASM 3.0

    综上所述,ASM作为一种强大的Java字节码操控工具,不仅能够简化代码生成的过程,还能够有效支持AOP的实现。通过使用ASM,开发人员可以更灵活地管理和增强代码,从而提高系统的可维护性和扩展性。随着AOP技术在软件...

    asm-bo-0.3.5.zip

    ASM是一个轻量级的Java字节码操控和分析框架,它可以直接用来动态生成和修改类或者作为其他代码生成工具的基础。在Android应用开发中,字节码层面的操作对于优化性能、调试、逆向工程或实现AOP(面向切面编程)等...

    my asm forger

    `asmforger.h`是ASM Forger的核心头文件,它定义了ASM Forger的主要类和方法,如ASM代码生成器、反编译器等。这些类是ASM Forger功能的抽象,使得开发者能够方便地调用和扩展工具的功能。 `execute_fun.h`同样是一...

    asm 3.3.1 jar

    ASM库的设计目标是能够快速地读取、修改和生成Java字节码,这使得它成为许多动态语言运行时环境、代码分析工具和AOP(面向切面编程)框架的理想选择。ASM提供了一套底层API,开发者可以直接对字节码进行操作,而无需...

    ASM3.0指南翻译

    它旨在最小化运行时动态生成Class和转换对应用程序速度的影响,这得益于其高度优化的内部机制和精炼的代码库。这种设计思路使得ASM成为执行速度最快的Java字节码操作工具之一。 **2.3 开放的社区与许可** ASM3.0...

    cglib.jar和cglib代理必备的asm所有jar

    Cglib是一个强大的高性能的代码生成库,它在Java运行时动态地为一个类创建子类,从而实现对这个类的功能扩展。Cglib通常被用来做AOP(面向切面编程)的底层实现,例如Spring AOP就是利用Cglib来实现无侵入式的代理。...

    auto java代码生成器 google的子项目

    代码生成器可能利用元编程技术来动态创建类、方法或其他代码结构。 5. **代码分析**:在自动生成代码前,工具需要理解现有代码的结构和模式。这通常涉及代码解析、抽象语法树(AST)构建以及静态分析。通过分析,...

    Java的一个源代码生成集合

    总的来说,Java的源代码生成集合是开发者的重要利器,它们能够帮助我们快速构建应用,降低出错概率,提高代码质量,同时让开发者更专注于业务逻辑而非繁琐的模板代码。合理利用这些工具,可以使开发工作更加高效和...

    asm-commons-3.3.jar

    6. **实战应用**:ASM Commons常用于ORM框架(如Hibernate)、AOP框架(如AspectJ)、代码生成工具(如MyBatis Generator)以及动态代理库(如CGLIB)等,它为这些框架和工具提供了底层的支持,帮助实现诸如动态类...

    BITEK ASM2 v0140

    《BITEK ASM2 v0140:BIT1612编程利器解析》 在IT行业中,特定的工具往往能提升开发效率,BITEK ASM2 v0140便是其中的一员。这款工具专为BIT1612处理器设计,提供了一套强大的汇编器功能,帮助程序员进行高效且精确...

    asm-3.2-bin.zip

    总的来说,ASM-3.2-bin.zip是开发者进行深度字节码操作的利器,适用于那些需要对Java字节码有精细控制的场合,比如动态代理框架、代码生成工具、性能监控以及安全防护等场景。了解并熟练掌握ASM,将极大地扩展Java...

    cglib和asm jar包

    总的来说,CGLIB和ASM都是Java开发中的利器,它们让开发者能够在运行时对类进行动态扩展和修改,极大地丰富了Java编程的可能性。在学习和使用这两个库时,需要理解Java字节码的基础知识,以及如何利用它们实现特定的...

    c32asm.反编译工具

    C32ASM就是针对这种格式的二进制文件进行反编译的利器,它能够将难以理解的机器指令转化为更易读的汇编语言。 C32ASM的核心功能在于其反编译能力。反编译是将机器码转化为汇编语言的过程,这个过程并非简单的翻译,...

    xbean-asm-util-3.18.zip

    《XBean Asm Util 3.18与RoboVM Maven Plugin:开源项目的构建利器》 在IT领域,高效和灵活的开发工具是至关重要的。本文将深入探讨两个开源项目——"XBean Asm Util 3.18"和"RoboVM Maven Plugin",它们都是开发者...

    Java虚拟机字节码.zip

    1. **ASM框架**: ASM是一个Java字节码操控和分析框架,常用于动态代理、AOP(面向切面编程)和代码生成。通过ASM,程序员可以直接操作字节码,生成和转换Java类。ASM提供了一套详细的API,允许开发人员在字节码级别...

    asm 编程软件soft.zip dosbox debug vim三个软件

    总结来说,“asm编程软件soft.zip”包含的DOSBox、Debug和Vim是汇编开发的三大利器。DOSBox提供了一个运行DOS程序的平台,Debug提供了强大的调试功能,而Vim则为编写和编辑汇编代码提供了优秀的环境。这三个工具的...

    asmtools.zip

    对于开发者来说,了解和操作字节码能够帮助我们深入理解程序的运行机制,进行性能优化,甚至实现动态代码生成等高级功能。asmtools.zip提供了一个强大的工具集,它包含了对Java字节码进行操作的asmtools包,版本为...

    cglib-3.0.rar

    CGlib,全称为Code Generation Library,是一个强大的、高性能的代码生成库,被广泛应用于Java世界中,尤其是动态代理领域。CGlib的核心功能是能够在运行时动态创建类的子类,进而实现对目标类的方法增强或拦截。在...

    智能反汇编程序DASM51

    4. **生成ASM51兼容源程序**:可以从目标码程序生成符合ASM51汇编语言格式的源程序,并将其写入磁盘。 5. **灵活的反汇编方式**:支持指定地址进行顺序反汇编或显示目标码(16进制码或ASCII码两种方式),为操作者...

    Java 反射-动态代理

    CGLIB是基于ASM库,通过字节码技术动态生成子类来实现代理。Spring框架默认使用CGLIB作为AOP(面向切面编程)的底层实现。 动态代理的应用场景广泛,包括: - AOP:在不修改源代码的情况下,为方法添加预处理和后...

Global site tag (gtag.js) - Google Analytics