怎么修改字节(应用修改字节码的方法)

中国论文网 发表于2022-11-17 21:21:55 归属于电子论文 本文已影响493 我要投稿 手机版

       中国论文网为大家解读本文的相关内容:          

摘 要:ASM作为一种修改JAVA二进制字节码的方法已经被很多的项目使用,它提供了灵活多变的API,在监控应用和辅助应用日志等方面有着不可替代的地位,本文将以网络购物车结算业务为例,简单分析一下ASM是如何完成JAVA字节码的修改。

关键词:AOP;ASM
  引言:面向对象编程方式下,业务逻辑代码中经常会耦合一些如:权限校验、事务控制和日志记录等代码,这些代码有时会影响我们业务代码的可读性,而且有时可能会影响到业务代码的健壮性和正确性,例如,一个日志代码若向外抛出异常则可能影响到整个业务方法的执行失败。从代码的复用性角度看,大量一样的日志代码存在于不同的业务方法中,这样对于日志代码的维护成本也是相当高的。之所以会存在上述问题,主要是面向对象编程方式下,主要关心的是类与类之间的纵向关系,即一个类的父类是谁或一个类被那些类继承。而对于同一层次上的类之间存在什么样子的关系基本不关注,而面向切面编程更关注的是系统横向之间的关系,在类的每个方法周围组织类与类之间的关系。对于面向切面编程实现技术有很多种,但核心都是修改JAVA字节码,大体上可以分为两类:编译器修改字节码和运行期修改字节码。本文只讨论ASM在运行期修改字节码技术(注:ASM也可以在编译期修改字节码)。下面以网络购物车为例来展示一下ASM技术对AOP(Aspect Oriented Programming,面向切面编程)的支持。
1、网络购物车结算业务功能介绍:
  用户在网上选购完自己喜欢的商品后,可以在购物车界面查看到自己已经选购的商品,并且还可以在页面最下面点击结算按钮完成本次购物,结算后会跳转到结算成功页面,并显示实际的付款金额。
第一, 由于是网络购物系统,为了安全我们在网页当中隐藏了一个令牌,每次用户提交时都会去校验当前提交的请求中的令牌是否有效,若有效才可以进行余下的操作。
第二, 每次用户的操作都要做相应的业务日志的记录,这就要求我们的业务代码执行完毕后可以记录相应的操作日志。
总体上完成这样的功能,按面向对象编程方式,基本上是会把校验令牌和日志记录耦合在业务代码中。
技术介绍:
  ASM是一个JAVA字节码分析、创建和修改的开源应用框架。在ASM中提供了诸多的API用于对类的内容进行字节码操作的方法。与传统的BCEL和SERL不同,在ASM中提供了更为优雅和灵活的操作字节码的方式。目前ASM已被广泛的开源应用架构所使用,例如:Spring、Hibernate等,ASM可以分析一个类、从字节码角度创建一个类和修改已经编译过的字节码文件。ASM提供了两组API:Core和Tree,Core是基于访问者模式来操作类,而Tree是基于树节点来操作类,本文我们主要使用CoreAPI,ASM CoreAPI的核心接口是ClassVisitor、ClassWriter和XXXVisitor(AnnotationVisitor、FieldVisitor、MethodVisitor等)
2.1 ClassVisitor:这个类会提供你要转变的类的字节数组,它的accept方法,接受一个具体的ClassVisitor,并调用实现中具体的visit,visitSource, visitOuterClass, visitAnnotation, visitAttribute, visitInnerClass, visitField, visitMethod和 visitEnd方法。
2.2 ClassWriter:ClassVisitor的一个实现类,这个类中的toByteArray方法会将最终修改的字节码以byte数组形式返回,在这个类的构造时可以指定让系统自动为我们计算栈和本地变量的大小(COMPUTE_MAXS),也可以指定系统自动为我们计算栈帧的大小(COMPUTE_FRAMES)。
2.3 XXXVisitor: 接口定义了和属性结构相对应的方法,这些方法可以操作属性、方法和注释等
ASM的操作流程,首先是获得其二进制的字节码,即用ClassReader来读取一个类,然后需要一个能将二进制字节码写回的类,即用ClassWriter类,最后就是一个事件过滤器,即ClassAdapter。事件过滤器中的某些方法可以产生一个新的XXXVisitor对象,当我们需要修改对应的内容时只要实现自己的XXXVisitor并返回,最终会形成新的二进制代码。
3、用ASM实现网络购物车结算功能:
  业务功能介绍时,已经给出了相应的要实现内容,这里我们主要关注的是如何用ASM去实现令牌的校验和日志的记录,对于业务数据的操作不是我们所关注的,因为他们用原有的面向对象方式完全可以实现。
首先我们需要一个核心业务类,即将用户所选的商品的价格进行求和并记录到数据库中,这个类定义如下:
  class BalanceService{
  ...
   public BigDecimal balance(List goodsList){
   //计算价格和
   BigDecimal sum = new BigDecimal(0);
   for(Goods goods:goodsList){
   (ce());
   }
   lanceSum(sum);
   return sum;
   }
  ...
}

期望得到的类应该是如下类型:
  class BalanceService{
  ...
   public BigDecimal balance(List goodsList){
   //校验令牌
   Method1();
   //计算价格和
   BigDecimal sum = new BigDecimal(0);
   for(Goods goods:goodsList){
   (ce());
   }
   lanceSum(sum);
   //记录日志
   Method2();
   return sum;
   }
  ...
}
Method1和Method2是要在运行期动态植入的,要植入这两个方法就需要一个方法MethodVisitor,由于MethodVisitor是一个接口所以我们要实现这个接口,幸运的是ASM为我们提供了一个简单的实现MethodAdapter,我们只要继承这个类且实现visitCode,在visitCode方法中添加方法Method1内要的操作即可实现将Method1的操作植入到balance方法中。要植入Method2方法内要操作的内容可以实现visitInsn方法,visitInsn方法它是ASM访问到无参数指令时调用的,我们balance方法执行的最后一个指令是“return”指令,这个指令在JVM虚拟机的指令集中是一个无参指令,所以我们只要判断当前调用visitInsn传递的指令是不是” return”指令即可,若是则调用修改相应的字节码代码。在应用中提供一个自定义的类加载器这样就可以在类加载时嗲用相应的ModifyMethodClassAdapter方法来动态的实现对balance方法的修改。这样完成了将无关的业务代码动态植入的目的。
4、总结:
  ASM对JAVA字节码的动态修改可以将业务代码和非业务代码进行了很好的解耦,彼此之间相互独立,互不影响,而且可以根据需要动态植入和卸载。但是这也产生了一个问题,由于是动态修改字节码,这样代码出现问题时将给分析问题带来一定的麻烦,而且在性能上也有一定的牺牲,因此对于要求响应效率较高的应用不适合使用这种动态修改字节码得方式。总之,正确的场合去使用ASM还是可以给我们带来很好的收益,不正确的场合可能就会有事倍功半的效果。

  中国论文网(www.lunwen.net.cn)免费学术期刊论文发表,目录,论文查重入口,本科毕业论文怎么写,职称论文范文,论文摘要,论文文献资料,毕业论文格式,论文检测降重服务。

返回电子论文列表
展开剩余(