深度探讨Java字节代码的操纵方法
51CTO编者按:我们曾给大家引见过Java字节码文件操作技巧。 Java作为业界运用最为广泛的言语之一, 深得众多软件厂商和开发者的推崇, 但是对于Java言语的深度理解和运用, 毕竟是很少会有人触及的话题。 希望读者们喜欢。 开发人员运用Java的方式比较简单。 编写Java源代码, 再利用IDE提供的功能直接运转Java顺序就可以了。 java), IDE会担任调用Java的编译器把Java源代码编译成平台有关的字节代码(bytecode), class)。 Java通过这种方式来实现其“编写一次, Java类文件中包含的字节代码可以被不同平台上的JVM所运用。 也可以通过网络方式来下载, JVM中的类加载器会担任从包含字节代码的字节数组(byte[])中定义出Java类。 或是对已有的Java字节代码停止修正。 这个时分就需要用到本文中将要引见的相关技术。 首先引见一下如何动态编译Java源文件。 动态编译Java源文件 在一般情况下, 对有些运用来说, 再由JVM来加载执行。 典型的场景是很多算法竞赛的在线评测零碎(如PKUJudgeOnline), 允许用户上传Java代码, 由零碎在后台编译、运转并停止判定。 运用的做法是直接在顺序中调用Java编译器。 JSR199引入了Java编译器API。 可以通过此API来动态编译Java代码。 该Java类的代码是保管在一个字符串中的。 如果不能运用JDK6提供的Java编译器API的话, 可以运用JDK中的工具类com. sun. javac. 不过该工具类只能编译寄存在磁盘上的文件, 另外一个可用的工具是EclipseJDTCore提供的编译器。 支持运转和调试有错误的代码。 Play框架在外部运用了JDT的编译器来动态编译Java源代码。 在开发形式下, Play框架会活期扫描项目中的Java源代码文件, 一旦发现有修正, 因而在修正代码之后, 刷新页面就可以看到变化。 jar在运用的CLASSPATH中。 下面引见一个例子, 是关于如何在Java里面做四则运算, 一般的做法是剖析输出的运算表达式, 自己来模拟计算过程。 考虑到括号的存在和运算符的优先级等成绩, 这样的计算过程会比较复杂, 而且容易出错。 直接把输出的表达式当做JavaScript或是JavaFX脚本来执行, 失掉结果。 下面的代码运用的做法是动态生成Java源代码并编译, 接着加载Java类来执行并获取结果。 下面的代码给出了运用动态生成的Java字节代码的基本形式, 再通过Java反射API来调用对象中的方法。 Java字节代码增强 Java字节代码增强指的是在Java字节代码生成之后, 在很多Java框架中都可以见到这种实现方式。 注解在Java源代码中声明了需要增强的行为及相关的元数据, 由框架在运转时刻完成对字节代码的增强。 一般都集中在减少冗余代码和对开发人员屏蔽底层的实现细节上。 并且难以维护。 而通过字节代码增强, 开发人员只需要声明Bean中的属性即可, 用过JPA的人, 在调试顺序的时分, 会发理想体类中被添加了一些额外的域和方法。 字节代码增强在面向方面编程(AOP)的一些实现中也有运用。 在讨论如何停止字节代码增强之前, 类文件 如上所示, 如所实现的接口、域、方法和属性等, 是以数组来表示的。 而在数组之前的是该数组中条目的个数。 有其不同的外部结构。 对于开发人员来说, 直接操纵包含字节代码的字节数组的话, 这些类库包括ASM、cglib、serp和BCEL等。 比如考虑下面一个简单的需求, 在一个Java类的所有方法执行之前输出相应的日志。 如果运用ASM的话, 可以把字节代码写回磁盘或是由类加载器直接运用。 增强局部的逻辑比较简单, 只是遍历Java类中的所有方法并添加对System. out. println方法的调用。 在字节代码中, Java方法体是由一系列的指令组成的。 而要做的是生成调用System. 可以打印出Java类的字节代码的结构信息。 当需要增强某个类的时分, 可以先在源代码上做出修正, 比较常见的做法有: 在构建过程中完成, 比如通过Ant或Maven来执行相关的操作。 当获取到Java类的字节代码之后, 先停止增强处理, 再从修正过的字节代码中定义出Java类。 由于存在着大量对Java字节代码停止修正的需求, JDK5引入了java. 基本的思路是在JVM启动的时分添加一些代理(agent)。 每个代理是一个jar包, 其清单(manifest)文件中会指定一个代理类。 这个类会包含一个premain方法。 JVM在启动的时分会首先执行代理类的premain方法, 在premain方法中就可以对顺序本身的字节代码停止修正。 java. instrument包支持两种修正的场景, 相当于前面提到的类字节代码增强。 lang. instrument. ClassFileTransformer接口来完成对已有Java类的转换。 把该代理类打成一个jar包, 并在jar包的清单文件中通过Premain-Class声明代理类的称号。 添加JVM启动参数-javaagent:myagent. jar。 JVM会在加载Java类的字节代码之前, 完成相关的转换操作。 通过它, 非常适合于功能剖析、调试跟踪和日志记载等任务。 字节代码增强和源代码生成是不同的概念。 就已经成为了顺序的一局部, 要么重新生成。 对于开发人员是完全透明的。 妥善运用Java字节代码的操纵技术, 可以更好的解决某一类开发成绩。 【编辑引荐】