前些日工作中需要改一个jar包中的程序代码,想了很多方法 包括直接修改、动态代理等等,但都效果不好。最后无意中发现了ASM这个框架,感觉正是我需要的。研究几日,用到一些基础功能就实现了所要效果,所以写出来给大家共享,自己忘了也好参考参考.下面是ASM一些基本介绍,就当是抛砖引玉了~~其中参考了几篇其他iteye朋友的文章,主要是JVM、字节码和类加载方面的东西,有助于更好的理解ASM。这里我主要说说ASM的东西,其他的大家去看看吧。
一、什么是ASM
ASM是一个java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
二、ASM核心类介绍:
①ClassReader类:这个类会提供你要转变的类的字节数组,它的accept方法,接受一个具体的ClassVisitor,并调用实现中具体的visit,visitSource, visitOuterClass, visitAnnotation, visitAttribute, visitInnerClass, visitField,visitMethod和 visitEnd方法。
②ClassWriter类:这个类是ClassVisitor的一个实现类,这个类中的toByteArray方法会将最终修改的字节码以byte数组形式返回,在这个类的构造时可以指定让系统自动为我们计算栈和本地变量的大小(COMPUTE_MAXS),也可以指定系统自动为我们计算栈帧的大小(COMPUTE_FRAMES)。
③ClassAdapter类:这个类也是ClassVisitor的一个实现类,这个类可以看成是一个事件过滤器,在这个类里,它对ClassVisitor的实现都是委派给一个具体的ClassVisitor实现类,即调用那个实现类实现的方法。
④FieldVisitor接口:这个接口定义了和属性结构相对应的方法,这些方法可以操作属性。
⑤MethodVisitor接口:这个接口定义了和方法结构相对应的方法,这些方法可以去操作源方法,具体的可以查看一下源码。
三、操作流程:
一般情况下,我们需要操作一个类时,首先是获得其二进制的字节码,即用ClassReader来读取一个类,然后需要一个能将二进制字节码写回的类,即用ClassWriter类,最后就是一个事件过滤器,即ClassAdapter。事件过滤器中的某些方法可以产生一个新的XXXVisitor对象,当我们需要修改对应的内容时只要实现自己的XXXVisitor并返回就可以了。
四、程序示例:
所需开发包:asmSDK
所需开发包:asmSDK
功能:使用ASM在Person.class文件上动态添加一条打印语句
开发工具:jdk6、 Myeclipse
下面是程序的树形图
Person.java文件代码如下:
public class Person { public final String sleep() { try { Thread.sleep(2);//沉睡两秒 } catch (InterruptedException e) { e.printStackTrace(); } return "123"; } }
ModifyMethodClassAdapter.java代码如下:
package com.asm1; import org.objectweb.asm.ClassAdapter; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; public class ModifyMethodClassAdapter extends ClassAdapter { public ModifyMethodClassAdapter(ClassVisitor cv) { super(cv); } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { //(Ljava/lang/String;[BII)Ljava/lang/Class; //(Ljava/lang/String;[BIILjava/security/ProtectionDomain;)Ljava/lang/Class; int a=10; if (name.equals("sleep")&&desc.equals("()Ljava/lang/String;")) { return new ModifyMethod(super.visitMethod(access, name, desc, signature, exceptions), access, name, desc); } //System.out.println(a); return super.visitMethod(access, name, desc, signature, exceptions); } @Override public void visitEnd() { cv.visitField(Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC, "timer", "J", null, null); } }注意name.equals("sleep")&&desc.equals("()Ljava/lang/String;")这行,ASM会根据方法名和方法描述来查找方法,下面是方法描述在类中和二进制中的对应关系
|
ModifyMethod.java代码如下:
package com.asm1; import org.objectweb.asm.MethodAdapter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; public class ModifyMethod extends MethodAdapter { public ModifyMethod(MethodVisitor mv, int access, String name, String desc) { super(mv); } @Override public void visitCode() { mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); mv.visitLdcInsn("我是赛亚人"); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V"); } }
代码中各方法
visitFieldInsn:访问一个属性的指令,该指令负责加载或储存一个对象的属性值
visitLdcInsn:将常量池中的字符串常量加载进栈顶
visitMethodInsn:访问方法的指令,该指令负责调用一个方法
visitLdcInsn:将常量池中的字符串常量加载进栈顶
visitMethodInsn:访问方法的指令,该指令负责调用一个方法
这里只用到三个方法,其他的可以看下ASM API文档
下面写了一个test类,里面是要生成的代码,
test.java代码如下:
public class test { public static void main(String[] args) { System.out.println("我是赛亚人"); } }
首先我们使用javap命令计算字节码指令:
javac test.java
javap -verbose test
生成字节码如下:
该文件是class的字节码指令集,可查看虚拟机字节码指令表来进一步了解。
可以看出ModifyMethod.java的程序和该字节码是一一对应的。比如mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out","Ljava/io/PrintStream;");和
getstatic #2;//Field java/lang/System.out:Ljava/io/PrintStream;,只要我们使用javap计算出程序的字节码指令就可以使用ASM程序动态添加了。
测试类ModifyMethodTest.java代码如下:
ClassReader classReader = new ClassReader("com.asm1.Person"); //com.asm1.Person ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS); ClassAdapter classAdapter = new ModifyMethodClassAdapter(classWriter); classReader.accept(classAdapter, ClassReader.SKIP_DEBUG); byte[] classFile = classWriter.toByteArray(); File f = new File("F:\\abc\\aaa.class"); FileOutputStream fs = new FileOutputStream(f); fs.write(classFile); fs.close(); System.out.println("success");运行该程序,在abc文件夹下会生成一个aaa.class文件,使用反编译软件打开该文件如下:
如图所示,成功添加了一行打印语句。
说明使用ASM动态修改class成功!!
相关推荐
《汇编语言初探——以ARM架构为例》 在计算机科学的世界里,汇编语言作为底层编程的一种形式,是理解计算机工作原理的关键。本资源"asm.rar_arm"聚焦于ARM架构下的汇编语言学习,旨在为初学者提供一个入门的平台。...
【Linux核心初探】 Linux操作系统的核心是开源世界的重要组成部分,为开发者提供了深入理解操作系统底层机制的机会。这篇文档主要探讨了Linux内核的组织结构以及如何阅读和理解内核代码。 Linux内核源码的顶层目录...
AVR指令与汇编系统是微控制器领域中的一个重要概念,主要针对的是Atmel公司的AVR系列8位单片机,这些单片机采用RISC(Reduced Instruction Set Computer)架构,与传统的CISC(Complex Instruction Set Computer)...
《C2000处理器的Code Start Branch初探》 在嵌入式系统开发中,C2000系列微控制器以其高性能、低功耗的特点,广泛应用于工业控制、电力电子、电机驱动等领域。在使用C2000进行程序开发时,"Code Start Branch (CSB)...
13.MySQL Cluster实战初探 .pdf 14.SAP HANA深度剖析.pdf 15.eXtremeDB内存数据库性能提升方案分享.pdf 16.运用之妙 存乎一心—— Oracle优化器案例与算法解析.pdf 17.DM7 MPP架构——同时满足OLAP与OLTP需求.pdf 18...
MySQL Cluster实战初探 .pdf SAP HANA深度剖析.pdf eXtremeDB内存数据库性能提升方案分享.pdf 运用之妙 存乎一心—— Oracle优化器案例与算法解析.pdf DM7 MPP架构——同时满足OLAP与OLTP需求.pdf SAP 让大数据飞翔....
MySQL Cluster实战初探 .pdf SAP HANA深度剖析.pdf eXtremeDB内存数据库性能提升方案分享.pdf 运用之妙 存乎一心—— Oracle优化器案例与算法解析.pdf DM7 MPP架构——同时满足OLAP与OLTP需求.pdf SAP 让大数据飞翔....
MySQL Cluster实战初探 .pdf SAP HANA深度剖析.pdf eXtremeDB内存数据库性能提升方案分享.pdf 运用之妙 存乎一心—— Oracle优化器案例与算法解析.pdf DM7 MPP架构——同时满足OLAP与OLTP需求.pdf SAP 让大数据飞翔....
《电子-jpstm32asmfirst.zip:STM32汇编语言编程初探》 STM32系列微控制器是基于ARM Cortex-M内核的高性能、低功耗微处理器,广泛应用于单片机和嵌入式系统设计。在这个压缩包中,我们重点关注的是STM32的汇编语言...
《x86汇编语言:银行系统初探》 在计算机科学的世界里,汇编语言是一种底层编程语言,它与机器代码紧密相连,是程序员直接操控硬件的重要工具。本资源聚焦于x86架构下的汇编语言,通过一个简单的银行系统实例,为...
文件名:boot.asm ; 初始化函数 org 07c00h ; 告诉编译器将此段程序加载到内存0x0000:07C00处 mov ax, cs mov ds, ax mov es, ax call PrintStr ; 调用屏幕打印函数 jmp $ ; 无限循环 PrintStr: ; 屏幕...
随着时间的推移,里面积累的很多代码,大致用5个片段进行分类:扫一扫体验: ImitateFragment(模仿三方应用特效)仿新浪微博雷达扫描效果和卡片动画效果( )仿简书长按生成图片效果沉寝式模式初探仿简书头部...