Maison Java javaDidacticiel Étude approfondie du framework de bytecode Java ASM

Étude approfondie du framework de bytecode Java ASM

Jan 23, 2017 am 10:07 AM

一、什么是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();
 }
}
Copier après la connexion


生成一个类的字节码文件只需要用到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);
}
Copier après la connexion

注:一个编译后的java类不包含package和import段,因此在class文件中所有的类型都使用的是全路径。

示例2.修改类的字节码文件

C.java

package com.asm5;
 
public class C {
 public void m() throws InterruptedException{
 Thread.sleep(100);
 }
}
Copier après la connexion

将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();
 }
}
Copier après la connexion

为了弄清楚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);
 }
 }
  
}
Copier après la connexion


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();
 }
 }
 
}
Copier après la connexion

下面是一个测试类:

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));
 }
}
Copier après la connexion

输出结果为:100

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

更多java字节码框架ASM的深入学习相关文章请关注PHP中文网!

Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn

Outils d'IA chauds

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

AI Hentai Generator

AI Hentai Generator

Générez AI Hentai gratuitement.

Article chaud

R.E.P.O. Crystals d'énergie expliqués et ce qu'ils font (cristal jaune)
4 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Meilleurs paramètres graphiques
4 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Comment réparer l'audio si vous n'entendez personne
4 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: Comment déverrouiller tout dans Myrise
1 Il y a quelques mois By 尊渡假赌尊渡假赌尊渡假赌

Outils chauds

Bloc-notes++7.3.1

Bloc-notes++7.3.1

Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise

SublimeText3 version chinoise

Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1

Envoyer Studio 13.0.1

Puissant environnement de développement intégré PHP

Dreamweaver CS6

Dreamweaver CS6

Outils de développement Web visuel

SublimeText3 version Mac

SublimeText3 version Mac

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

Comment fonctionne le mécanisme de chargement de classe de Java, y compris différents chargeurs de classe et leurs modèles de délégation? Comment fonctionne le mécanisme de chargement de classe de Java, y compris différents chargeurs de classe et leurs modèles de délégation? Mar 17, 2025 pm 05:35 PM

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

Comment implémenter la mise en cache à plusieurs niveaux dans les applications Java à l'aide de bibliothèques comme la caféine ou le cache de goyave? Comment implémenter la mise en cache à plusieurs niveaux dans les applications Java à l'aide de bibliothèques comme la caféine ou le cache de goyave? Mar 17, 2025 pm 05:44 PM

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

Comment puis-je utiliser JPA (Java Persistance API) pour la cartographie relationnelle des objets avec des fonctionnalités avancées comme la mise en cache et le chargement paresseux? Comment puis-je utiliser JPA (Java Persistance API) pour la cartographie relationnelle des objets avec des fonctionnalités avancées comme la mise en cache et le chargement paresseux? Mar 17, 2025 pm 05:43 PM

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]

Comment utiliser Maven ou Gradle pour la gestion avancée de projet Java, la création d'automatisation et la résolution de dépendance? Comment utiliser Maven ou Gradle pour la gestion avancée de projet Java, la création d'automatisation et la résolution de dépendance? Mar 17, 2025 pm 05:46 PM

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.

Mar 17, 2025 pm 05:45 PM

L'article discute de la création et de l'utilisation de bibliothèques Java personnalisées (fichiers JAR) avec un versioning approprié et une gestion des dépendances, à l'aide d'outils comme Maven et Gradle.

See all articles