一、什么是ASM
ASM是一个java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
使用ASM框架需要导入asm的jar包,下载链接:asm-3.2.jar。
二、如何使用ASM
ASM框架中的核心类有以下几个:
① ClassReader:该类用来解析编译过的class字节码文件。
② ClassWriter:该类用来重新构建编译后的类,比如说修改类名、属性以及方法,甚至可以生成新的类的字节码文件。
③ ClassAdapter:该类也实现了ClassVisitor接口,它将对它的方法调用委托给另一个ClassVisitor对象。
示例1.通过asm生成类的字节码java 字节码框架 asm
package com.asm3; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; /** * 通过asm生成类的字节码 * @author Administrator * */ public class GeneratorClass { public static void main(String[] args) throws IOException { //生成一个类只需要ClassWriter组件即可 ClassWriter cw = new ClassWriter(0); //通过visit方法确定类的头部信息 cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT+Opcodes.ACC_INTERFACE, "com/asm3/Comparable", null, "java/lang/Object", new String[]{"com/asm3/Mesurable"}); //定义类的属性 cw.visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL+Opcodes.ACC_STATIC, "LESS", "I", null, new Integer(-1)).visitEnd(); cw.visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL+Opcodes.ACC_STATIC, "EQUAL", "I", null, new Integer(0)).visitEnd(); cw.visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL+Opcodes.ACC_STATIC, "GREATER", "I", null, new Integer(1)).visitEnd(); //定义类的方法 cw.visitMethod(Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT, "compareTo", "(Ljava/lang/Object;)I", null, null).visitEnd(); cw.visitEnd(); //使cw类已经完成 //将cw转换成字节数组写到文件里面去 byte[] data = cw.toByteArray(); File file = new File("D://Comparable.class"); FileOutputStream fout = new FileOutputStream(file); fout.write(data); fout.close(); } }
生成一个类的字节码文件只需要用到ClassWriter类即可,生成Comparable.class后用javap指令对其进行反编译:javap -c Comparable.class >test.txt ,编译后的结果如下:
public interface com.asm3.Comparable extends com.asm3.Mesurable { public static final int LESS; public static final int EQUAL; public static final int GREATER; public abstract int compareTo(java.lang.Object); }
注:一个编译后的java类不包含package和import段,因此在class文件中所有的类型都使用的是全路径。
示例2.修改类的字节码文件
C.java
package com.asm5; public class C { public void m() throws InterruptedException{ Thread.sleep(100); } }
将C.java类的内容改为如下:
package com.asm5; public class C { public static long timer; public void m() throws InterruptedException{ timer -= System.currentTimeMillis(); Thread.sleep(100); timer += System.currentTimeMillis(); } }
为了弄清楚ASM是如何实现的,我们先编译这两个类,然后比对它们的TraceClassVisitor的输出,我们可以发现如下的不同(粗体表示)
GETSTATIC C.timer : J
INVOKESTATIC java/lang/System.currentTimilis()J
LSUB
PUTSTATIC C.timer : J
LDC 100
INVOKESTATIC java/lang/Thread.sleep(J)V
GETSTATIC C.timer : J
INVOKESTATIC java/lang/System.currentTimilis()J
LADD
PUTSTATIC C.timer : J
RETURN
MAXSTACK=4
MAXLOCALS=1
通过比对上面的指令,我们可以发现必须在m()方法的最前面增加四条指令,在RETURN指令前也增加四条指令,同时这四条必须位于xRETURN和ATHROW之前,因为这些指令都会结束方法的执行。
具体代码如下:
AddTimeClassAdapter.java
package com.asm5; import org.objectweb.asm.ClassAdapter; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodAdapter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; public class AddTimeClassAdapter extends ClassAdapter { private String owner; private boolean isInterface; public AddTimeClassAdapter(ClassVisitor cv) { super(cv); } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { cv.visit(version, access, name, signature, superName, interfaces); owner = name; isInterface = (access & Opcodes.ACC_INTERFACE) != 0; } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions); if(!name.equals("<init>") && !isInterface && mv!=null){ //为方法添加计时功能 mv = new AddTimeMethodAdapter(mv); } return mv; } @Override public void visitEnd() { //添加字段 if(!isInterface){ FieldVisitor fv = cv.visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_STATIC, "timer", "J", null, null); if(fv!=null){ fv.visitEnd(); } } cv.visitEnd(); } class AddTimeMethodAdapter extends MethodAdapter{ public AddTimeMethodAdapter(MethodVisitor mv) { super(mv); } @Override public void visitCode() { mv.visitCode(); mv.visitFieldInsn(Opcodes.GETSTATIC, owner, "timer", "J"); mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J"); mv.visitInsn(Opcodes.LSUB); mv.visitFieldInsn(Opcodes.PUTSTATIC, owner, "timer", "J"); } @Override public void visitInsn(int opcode) { if((opcode>=Opcodes.IRETURN && opcode<=Opcodes.RETURN) || opcode==Opcodes.ATHROW){ mv.visitFieldInsn(Opcodes.GETSTATIC, owner, "timer", "J"); mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J"); mv.visitInsn(Opcodes.LADD); mv.visitFieldInsn(Opcodes.PUTSTATIC, owner, "timer", "J"); } mv.visitInsn(opcode); } @Override public void visitMaxs(int maxStack, int maxLocal) { mv.visitMaxs(maxStack+4, maxLocal); } } }
Generator.java
package com.asm5; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import org.objectweb.asm.ClassAdapter; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; public class Generator { public static void main(String[] args){ try { ClassReader cr = new ClassReader("com/asm5/C"); ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); ClassAdapter classAdapter = new AddTimeClassAdapter(cw); //使给定的访问者访问Java类的ClassReader cr.accept(classAdapter, ClassReader.SKIP_DEBUG); byte[] data = cw.toByteArray(); File file = new File(System.getProperty("user.dir") + "\\WebRoot\\WEB-INF\\classes\\com\\asm5\\C.class"); FileOutputStream fout = new FileOutputStream(file); fout.write(data); fout.close(); System.out.println("success!"); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
下面是一个测试类:
package com.asm5; public class Test { public static void main(String[] args) throws InterruptedException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { C c = new C(); c.m(); Class cc = c.getClass(); System.out.println(cc.getField("timer").get(c)); } }
输出结果为:100
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。
更多java字节码框架ASM的深入学习相关文章请关注PHP中文网!

本文討論了使用Maven和Gradle進行Java項目管理,構建自動化和依賴性解決方案,以比較其方法和優化策略。

本文使用Maven和Gradle之類的工具討論了具有適當的版本控制和依賴關係管理的自定義Java庫(JAR文件)的創建和使用。

本文討論了使用咖啡因和Guava緩存在Java中實施多層緩存以提高應用程序性能。它涵蓋設置,集成和績效優勢,以及配置和驅逐政策管理最佳PRA

本文討論了使用JPA進行對象相關映射,並具有高級功能,例如緩存和懶惰加載。它涵蓋了設置,實體映射和優化性能的最佳實踐,同時突出潛在的陷阱。[159個字符]

Java的類上載涉及使用帶有引導,擴展程序和應用程序類負載器的分層系統加載,鏈接和初始化類。父代授權模型確保首先加載核心類別,從而影響自定義類LOA


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

禪工作室 13.0.1
強大的PHP整合開發環境

SublimeText3 Linux新版
SublimeText3 Linux最新版

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

記事本++7.3.1
好用且免費的程式碼編輯器

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。