Étude approfondie du framework de bytecode Java ASM
一、什么是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中文网!

Outils d'IA chauds

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

AI Hentai Generator
Générez AI Hentai gratuitement.

Article chaud

Outils chauds

Bloc-notes++7.3.1
Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Le chargement de classe de Java implique le chargement, la liaison et l'initialisation des classes à l'aide d'un système hiérarchique avec Bootstrap, Extension et Application Classloaders. Le modèle de délégation parent garantit que les classes de base sont chargées en premier, affectant la classe de classe personnalisée LOA

L'article examine la mise en œuvre de la mise en cache à plusieurs niveaux en Java à l'aide de la caféine et du cache de goyave pour améliorer les performances de l'application. Il couvre les avantages de configuration, d'intégration et de performance, ainsi que la gestion de la politique de configuration et d'expulsion le meilleur PRA

L'article discute de l'utilisation de JPA pour la cartographie relationnelle des objets avec des fonctionnalités avancées comme la mise en cache et le chargement paresseux. Il couvre la configuration, la cartographie des entités et les meilleures pratiques pour optimiser les performances tout en mettant en évidence les pièges potentiels. [159 caractères]

L'article discute de l'utilisation de Maven et Gradle pour la gestion de projet Java, la construction de l'automatisation et la résolution de dépendance, en comparant leurs approches et leurs stratégies d'optimisation.
