Java深度理解——Java字节代码的操纵

但是关于Java言语的深度了解和运用, InfoQ中文站特地约请IBM初级工程师成富为大家撰写这个《Java深度历险》专栏, 旨在就Java的一些深度和初级特性分享他的经验。 开发人员运用Java的方式比拟复杂。 再利用IDE提供的功用直接运转Java顺序就可以了。 这种开发形式背后的进程是:开发人员编写的是Java源代码文件(. java), 以类文件的形式保存在磁盘上(. class)。 Java虚拟机(JVM)会负责把Java字节代码加载并执行。 Java经过这种方式来实现其“编写一次, runanywhere)”的目标。 Java类文件中包含的字节代码可以被不同平台上的JVM所运用。 还可以只存在于内存中。 JVM中的类加载器会负责从包含字节代码的字节数组(byte[])中定义出Java类。 可能会需要静态的生成Java字节代码, 这个时分就需要用到本文中将要引见的相关技术。 首先引见一下如何静态编译Java源文件。   静态编译Java源文件  在一般状况下, 开发人员都是在顺序运转之前就编写完成了全部的Java源代码并且成功编译。 这个时分就需要静态编译源代码来生成Java字节代码, 再由JVM来加载执行。 典型的场景是很多算法竞赛的在线评测系统(如PKUJudgeOnline), 运用的做法是直接在顺序中调用Java编译器。 假如运用JDK6的话, 可以经过此API来静态编译Java代码。 该Java类的代码是保存在一个字符串中的。   假如不能运用JDK6提供的Java编译器API的话, 可以运用JDK中的工具类com. sun. tools. javac. Main, 这是EclipseJava开发环境运用的增量式Java编译器, 支持运转和调试有错误的代码。 在开发形式下, 一旦发现有修改, 会自动编译Java源代码。 因此在修改代码之后, 刷新页面就可以看到变化。 运用这些静态编译的方式的时分, 需要确保JDK中的tools. jar在应用的CLASSPATH中。   下面引见一个例子, 是关于如何在Java里面做四则运算, 比方求出来(3+4)7-10的值。 一般的做法是分析输入的运算表达式, 这样的计算进程会比拟复杂, 另外一种做法是可以用JSR223引入的脚本言语支持, 失掉结果。 下面的代码运用的做法是静态生成Java源代码并编译, 这种做法完全运用Java来实现。 参见上一节  上面的代码给出了运用静态生成的Java字节代码的根本形式, 创立Java类的对象的实例, 再经过Java反射API来调用对象中的方法。 对其进行修改, 这种做法相当于对应用顺序的二进制文件进行修改。 在很多Java框架中都可以见到这种实现方式。 Java字节代码增强通常与Java源文件中的注解(annotation)一块运用。 一般都集中在增加冗余代码和对开发人员屏蔽底层的实现细节上。 用过JavaBeans的人可能对其中那些必须添加的getter/setter方法感到很繁琐, 在调试顺序的时分, 会发理想体类中被添加了一些额定的域和方法。 这些域和方法是在运转时刻由JPA的实现静态添加的。 首先引见一下表示一个Java类或接口的字节代码的组织形式。   类文件{  0xCAFEBABE, 小版本号, 大版本号, 常量池大小?  访问控制标记, 以后类信息, 父类信息, 实现的接口个数, 域个数, 方法个数, 方法信息数组, 属性个数, 属性信息数组  如上所示, 一个类或接口的字节代码运用的是一种松散的组织结构, 关于可能包含多个条目的内容, 而在数组之前的是该数组中条目的个数。 关于开发人员来说, 直接操纵包含字节代码的字节数组的话, 开发效率比拟低, 运用这些类库可以在一定水平上降低增强字节代码的复杂度。 比方思索下面一个复杂的需求, 在一个Java类的所有方法执行之前输出相应的日志。 可以用一个前增强(beforeadvice)来解决这个成绩。 假如运用ASM的话, 相关的代码如下:  从ClassWriter就可以获取到包含增强之后的字节代码的字节数组, 可以把字节代码写回磁盘或是由类加载器直接运用。 增强局部的逻辑比拟复杂, println方法的调用。 在字节代码中, Java方法体是由一系列的指令组成的。 out. println方法的指令, 并把这些指令插入到指令集合的最后面。 ASM提供了一个工具类ASMifierClassVisitor, 当需要增强某个类的时分, 可以先在源代码上做出修改,   对类文件进行增强的时机是需要在Java源代码编译之后, 在JVM执行之前。   实现自己的Java类加载器。 先进行增强处理, 再从修改正的字节代码中定义出Java类。 JDK5引入了java. lang. 其清单(manifest)文件中会指定一个代理类。 这个类会包含一个premain方法。 JVM在启动的时分会首先执行代理类的premain方法, JDK6中还允许在JVM启动之后静态添加代理。 lang. 一种是重定义一个Java类, 即完全交换一个Java类的字节代码;另外一种是转换已有的Java类, 相当于后面提到的类字节代码增强。 首先需要实现java. 就可以在代理的premain方法中运用它。   把该代理类打成一个jar包, 运转Java顺序的时分, 添加JVM启动参数-javaagent:myagent. jar。 这样的话, 完成相关的转换操作。 可以很容易的对二进制分发的Java顺序进行修改, 十分合适于功用分析、调试跟踪和日志记录等义务。 关于那些只是由于语法要求而添加的, 或是形式固定的代码, 完全可以将其字节代码静态生成出来。 字节代码增强和源代码生成是不同的概念。 开发人员需要去维护它:要么手工修改生成出来的源代码, 要么重重生成。 妥善运用Java字节代码的操纵技术,   【 宣布评论 条 】

posted on 2011-04-05 11:52  青青啊  阅读(218)  评论(0)    收藏  举报

导航