应用 ASM 实现 Java 说话的“多重持续”

    添加时间:2013-6-1 点击量:

    题目的提出


    在大项目组景象下,须要多重持续往往意味着糟糕的设计。但在处理惩罚一些遗留项目标时辰,多重持续可能是我们能做出的选择中价格小。因为 Java 说话本身不支撑多重持续,这经常会给我们带来麻烦,最后的成果可能就是多量的反复代码。本文试图应用 ASM 框架来解决这一题目。在扩大类的功能的同时,不产生任何反复代码。


    推敲如下的实际景象:有一组类,名为 SubClass1、SubClass2、SubClass3 和 SubClass4,它们共同持续了同一个父类 SuperClass。如今,我们须要这组类中的一项目组,例如 SubClass1 和 SubClass2,这两个类还要实现别的两个接口,它们分别为:IFibonacciComputer 和 ITimeRetriever。然而,这两个接口已经有了各自的实现类 FibonacciComputer 和 TimeRetriever。并且这两个类的实现逻辑就是我们想要的,我们不想做任何批改,只在 SubClass1 和 SubClass2 两个类中包含这些实现逻辑。


    它们的布局如图 1 所示:



    图 1. 布局类图
     


    因为 SubClass1,SubClass2 已经持续了 SuperClass,所以我们无法让它们再持续 FibonacciComputer 或 TimeRetriever。


    所以,想要它们再实现 IFibonacciComputer 和 ITimeRetriever 这两个接口,必定会产生反复代码。


    下面,我们就应用 ASM 来解决这个题目。


     

     


    Java class 文件格局以及类加载器介绍


    在后面的内容中,须要对 Java class 文件格局以及类加载器的常识有必然的懂得,所以这里先对这些内容做一个简单介绍:


    class 文件格局


    Java class 文件的布局如图 2 所示(图中“”默示呈现 0 次或随便率性多次):




    图 2.Java class 文件布局
     


    具体申明如下:



    • Magic Number: 每个 class 文件的前 4 个字节被称为“魔数”,它的内容为:0 xCAFEBABE。魔数的感化在于可以轻松地辨别出一个文件是不是 class 文件。

    • Version: 该项指明该 class 文件的版本号。

    • Constant Pool: 常量池是 class 文件中布局错杂,也首要的项目组。常量池包含了与文件中类和接口相干的常量。常量池中存储了诸如文字字符串,final 变量值。Java 虚拟机把常量池组织为进口列表的情势。常量池中很多进口都指向其他的常量进口,并且 class 文件中紧跟着常量池的很多条目也都邑指向常量池的进口。除了字面常量之外,常量池还可以容纳以下几种符号引用:类和接口的全限制名,字段的名称和描述符和办法的名称和描述符等。

    • Modifiers: 该项指明该文件中定义的是类还是接口,以及声明顶用了哪种润饰符,类或接口是私有的,还是公共的,类的类型是否是 final 的,等等。

    • This class: 该项是对常量池的索引。在这个地位,Java 虚拟性可以或许找到一个容纳了类或接口全限制名的进口。这里须要重视的是:在 class 文件中,所有类的全限制名都是以内部名称情势默示的。内部名称是将本来类全限制名中的“.”调换为“/”。例如:java.lang.String 的内部名称为 java/lang/String。

    • Super Class: 该项也是对常量池的索引,指了然该类超类的内部名称。

    • Interfaces: 该项指了然由该类直接实现或由接口扩大的父接口的信息。





    :Modifiers,This Class,Super Class 和 Interfaces 这四项的和就是一个类的声明项目组。



    • Annotation: 该项存储的是注解相干的内容,注解可能是关于类的,办法的以及字段的。

    • Attribute: 该项用来存储关于类,字段以及办法的附加信息。在 Java 5 引入了注解之后,该项目组内容几乎已经没有效处。

    • Field: 该项用来存储类的字段信息。






    • Method: 该项用来存储类的办法信息。





    类装载器介绍


    类装载器负责查找并装载类。每个类在被应用之前,都必须先经由过程类装载器装载到 Java 虚拟机傍边。Java 虚拟机有两种类装载器 :



    • 启动类装载器

      启动类装载器是 Java 虚拟机实现的一项目组,每个 Java 虚拟机都必须有一个启动类装载器,它知道怎么装载受信赖的类,比如 Java API 的 class 文件。






    • 用户自定义装载器

      用户自定义装载器是通俗的 Java 对象,它的类必须派生自 java.lang.ClassLoader 类。ClassLoader 类中定义的办法为法度供给了接见类装载机制的接口。










     

     


    ASM 简介以及编程模型


    ASM 简介


    ASM 是一个可以用来生成\转换和解析 Java 字节码的代码库。其他类似的对象还有 cglib、serp和 BCEL等。相较于这些对象,ASM 有以下的长处 :



    • ASM 具有简单、设计杰出的 API,这些 API 易于应用。

    • ASM 有很是杰出的开辟文档,以及可以帮助简化开辟的 Eclipse 插件

    • ASM 支撑 Java 6

    • ASM 很小、很快、很结实

    • ASM 有很大的用户群,可以帮助新手解决开辟过程中碰到的题目

    • ASM 的开源容许可以让你几乎以任何体式格式应用它





    编程模型


    ASM 供给了两种编程模型:



    • Core API,供给了基于事务情势的编程模型。该模型不须要一次性将全部类的布局读取到内存中,是以这种体式格式更快,须要更少的内存。但这种编程体式格式难度较大。

    • Tree API,供给了基于树形的编程模型。该模型须要一次性将一个类的完全布局全部读取到内存傍边,所以这种办法须要更多的内存。这种编程体式格式较简单。





    下文中,我们将只应用 Core API,是以我们只介绍与其相干的类。


    Core API 中把持字节码的功能基于 ClassVisitor 接口。这个接口中的每个办法对应了 class 文件中的每一项。Class 文件中的简单项的接见应用一个零丁的办法,办法参数描述了这个项的内容。而那些具有随便率性长度和错杂度的项,应用别的一类办法,这类办返回一个帮助的 Visitor 接口,经由过程这些帮助接口的对象来完成具体内容的接见。例如 visitField 办法和 visitMethod 办法,分别返回 FieldVisitor 和 MethodVisitor 接口的对象。


    清单 1 为 ClassVisitor 中的办法列表:




    清单 1.ClassVisitor 接口中的办法






    				
    
    public interface ClassVisitor {
    // 接见类的声明项目组
    void visit(int version, int access, String name, String
    signature,String superName, String[] interfaces);
    // 接见类的代码
    void visitSource(String source, String debug);
    // 接见类的外部类
    void visitOuterClass(String owner, String name, String desc);
    // 接见类的注解
    AnnotationVisitor visitAnnotation(String desc, boolean visible);
    // 接见类的属性
    void visitAttribute(Attribute attr);
    // 接见类的内部类
    void visitInnerClass(String name, String outerName,
    String innerName,int access);
    // 接见类的字段
    FieldVisitor visitField(int access, String name, String desc,
    String signature, Object value);
    // 接见类的办法
    MethodVisitor visitMethod(int access, String name,
    String desc,String signature, String[] exceptions);
    // 接见停止
    void visitEnd();
    }

    彼此相爱,却不要让爱成了束缚:不如让它成为涌动的大海,两岸乃是你们的灵魂。互斟满杯,却不要同饮一杯。相赠面包,却不要共食一个。一起歌舞欢喜,却依然各自独立,相互交心,却不是让对方收藏。因为唯有生命之手,方能收容你们的心。站在一起却不要过于靠近。—— 纪伯伦《先知》
    分享到: