什么是ASM树API: ASM树API是ASM的一部分,可让您创建/修改内存中的类。 该类被视为信息树。 像整个类一样,它是ClassNode的实例,其中包含FieldNode对象列表,MethodNode对象列表等。本文假设读者已经在这里阅读了第一部分。

通过树API的简单类:让我们使用树API创建我们的第一类。 同样,我将直接进入一个代码示例,因为没有什么比代码示例更好。 生成的类具有打印“ Hello World!”的主要方法。

TreeAPIDemo.java

package com.geekyarticles.asm;import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;public class TreeAPIDemo {public static void main(String [] args) throws Exception{ClassNode classNode=new ClassNode(4);//4 is just the API version number//These properties of the classNode must be setclassNode.version=Opcodes.V1_6;//The generated class will only run on JRE 1.6 or aboveclassNode.access=Opcodes.ACC_PUBLIC;classNode.signature="Lcom/geekyarticles/asm/Generated;";classNode.name="com/geekyarticles/asm/Generated";classNode.superName="java/lang/Object";//Create a methodMethodNode mainMethod=new MethodNode(4,Opcodes.ACC_PUBLIC|Opcodes.ACC_STATIC,"main", "([Ljava/lang/String;)V",null, null);mainMethod.instructions.add(new FieldInsnNode(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"));mainMethod.instructions.add(new LdcInsnNode("Hello World!"));mainMethod.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V"));mainMethod.instructions.add(new InsnNode(Opcodes.RETURN));//Add the method to the classNodeclassNode.methods.add(mainMethod);//Write the classClassWriter cw=new ClassWriter(ClassWriter.COMPUTE_MAXS|ClassWriter.COMPUTE_FRAMES);classNode.accept(cw);//Dump the class in a fileFile outDir=new File("out/com/geekyarticles/asm");outDir.mkdirs();DataOutputStream dout=new DataOutputStream(new FileOutputStream(new File(outDir,"Generated.class")));dout.write(cw.toByteArray());dout.flush();dout.close();}
}

如您所见,代码非常简单。 与BCEL相比,它的主要优点是与BCEL不同,ASM不需要您将每个常量显式添加到常量池中。 相反,ASM会照顾常量池本身。

读取类文件: ClassNode是ClassVisitor。 因此,读取用于树API的类就像创建ClassReader对象并使用它读取类文件一样简单,同时将ClassNode对象作为其accept方法传递给参数。 完成此操作后,将通过类中存在的所有信息完全初始化传递的ClassNode。 在下面的示例中,我们将打印类中的所有方法。

TreeAPIClassReaderDemo.java

package com.geekyarticles.asm;import java.io.FileInputStream;
import java.io.InputStream;import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;public class TreeAPIClassReaderDemo {public static void main(String[] args) throws Exception{InputStream in=new FileInputStream("out/com/geekyarticles/asm/Generated.class");ClassReader cr=new ClassReader(in);ClassNode classNode=new ClassNode();//ClassNode is a ClassVisitorcr.accept(classNode, 0);//Let's move through all the methodsfor(MethodNode methodNodeclassNode.methods){System.out.println(methodNode.name+"  "+methodNode.desc);}}}

修改类文件:修改类文件是上述两个过程的组合。 我们首先以通常的方式读取该类,对数据进行必要的更改,然后将其写回到文件中。 以下程序实现了一些日志代码的自动注入。 当前,我们的Logger类仅打印到标准输出。 @Loggable注释的每个方法在开始和返回时都将被记录。 在此,我们不记录throw-exception。 但是,也可以通过检查操作码ATHROW以相同的方式实现。

LoggingInsertion.java

package com.geekyarticles.asm;import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Iterator;import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;public class LoggingInsertion {public static void main(String[] args) throws Exception{InputStream in=LoggingInsertion.class.getResourceAsStream("/com/geekyarticles/asm/LoggingTest.class");ClassReader cr=new ClassReader(in);ClassNode classNode=new ClassNode();cr.accept(classNode, 0);//Let's move through all the methodsfor(MethodNode methodNodeclassNode.methods){System.out.println(methodNode.name+"  "+methodNode.desc);boolean hasAnnotation=false;if(methodNode.visibleAnnotations!=null){for(AnnotationNode annotationNodemethodNode.visibleAnnotations){if(annotationNode.desc.equals("Lcom/geekyarticles/asm/Loggable;")){hasAnnotation=true;break;}}}if(hasAnnotation){//Lets insert the begin loggerInsnList beginList=new InsnList();beginList.add(new LdcInsnNode(methodNode.name));beginList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/geekyarticles/asm/Logger", "logMethodStart", "(Ljava/lang/String;)V"));Iterator<AbstractInsnNode> insnNodes=methodNode.instructions.iterator();while(insnNodes.hasNext()){System.out.println(insnNodes.next().getOpcode());}methodNode.instructions.insert(beginList);System.out.println(methodNode.instructions);//A method can have multiple places for return//All of them must be handled.insnNodes=methodNode.instructions.iterator();while(insnNodes.hasNext()){AbstractInsnNode insn=insnNodes.next();System.out.println(insn.getOpcode());if(insn.getOpcode()==Opcodes.IRETURN||insn.getOpcode()==Opcodes.RETURN||insn.getOpcode()==Opcodes.ARETURN||insn.getOpcode()==Opcodes.LRETURN||insn.getOpcode()==Opcodes.DRETURN){InsnList endList=new InsnList();endList.add(new LdcInsnNode(methodNode.name));endList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/geekyarticles/asm/Logger", "logMethodReturn", "(Ljava/lang/String;)V"));methodNode.instructions.insertBefore(insn, endList);}}}}//We are done now. so dump the classClassWriter cw=new ClassWriter(ClassWriter.COMPUTE_MAXS|ClassWriter.COMPUTE_FRAMES);classNode.accept(cw);File outDir=new File("out/com/geekyarticles/asm");outDir.mkdirs();DataOutputStream dout=new DataOutputStream(new FileOutputStream(new File(outDir,"LoggingTest.class")));dout.write(cw.toByteArray());dout.flush();dout.close();}}

LoggingTest.java

package com.geekyarticles.asm;public class LoggingTest {public static void run1(){System.out.println("run 1");}@Loggablepublic static void run2(){System.out.println("run 2");}@Loggablepublic static void main(String [] args){run1();run2();}
}

记录器

package com.geekyarticles.asm;public class Logger {public static void logMethodStart(String methodName){System.out.println("Starting method: "+methodName);}public static void logMethodReturn(String methodName){System.out.println("Ending method: "+methodName);}
}

Loggable.java

package com.geekyarticles.asm;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Loggable {}

如果运行此程序,则生成的文件将依赖于Logger类。 手动将Logger类复制到out目录中的正确软件包。 如果运行生成的类(它是LoggingTest类的修改版本),则将输出以下内容。

bash-4.1$ java  com.geekyarticles.asm.LoggingTest
Starting method: main
run 1
Starting method: run2
run 2
Ending method: run2
Ending method: main

请注意,与普通列表不同,在迭代InsnList对象时可以对其进行修改。 任何更改都会立即反映出来。 因此,如果在当前位置之后插入了一些指令,则也将对其进行迭代。

参考: 使用ASM 4处理Java类文件–第二部分: JCG合作伙伴提供的 Tree API   极客文章博客上的Debasish Ray Chawdhuri。

翻译自: https://www.javacodegeeks.com/2012/02/manipulating-java-class-files-with-asm_22.html

使用ASM 4处理Java类文件–第二部分:Tree API相关推荐

  1. java asm tree_使用ASM 4处理Java类文件–第二部分:Tree API

    java asm tree 什么是ASM树API: ASM树API是ASM的一部分,可让您创建/修改内存中的类. 该类被视为信息树. 像整个类一样,它是ClassNode的实例,其中包含FieldNo ...

  2. 使用ASM 4处理Java类文件–第一部分:世界,您好!

    什么是ASM :ASM是一个用于处理Java字节码的开源Java库. 因此,它具有与Apache BCEL相同的目的. 由于本文假定读者具有Java类文件格式的某些知识,因此建议在此处进行阅读. 那么 ...

  3. 【深入理解JVM】Java类文件的基本结构

    Java类文件(.class文件)是一个为已编译Java程序仔细定义的格式.Java源代码被编译成能够被任何JVM加载和执行的类文件.在被JVM加载之前,类文件可能是由网络传输而来. 类文件是独立于底 ...

  4. java 类文件_Java类文件概述

    所谓 Java 类文件,就是通常用 javac 编译器产生的 .class 文件.这些文件具有严格定义的格式. Java 源文件经过 javac 编译器编译之后,将会生成对应的二进制文件(如下图所示) ...

  5. java 使用不同目录下的类_如何运行在不同目录下的java类文件? - Break易站

    Java 基础语法 在本文中,我们将学习如何使用其他项目的实用程序,类和成员.在继续之前,让我们了解一些关键字. 类路径 类路径是jvm开始执行程序的位置.与传统的动态加载行为类似,当执行Java程序 ...

  6. java javap_javap的用途不断发展:您的Java类文件中隐藏了什么?

    java javap 什么是Javap,如何使用它以及何时要反汇编类文件? 作为Java开发工具包(JDK)的一部分,我们可以使用许多工具,这些工具有助于更好地理解Java代码. 这些工具之一是jav ...

  7. javap的用途不断发展:您的Java类文件中隐藏了什么?

    什么是Javap,如何使用它以及何时要反汇编类文件? 作为Java开发工具包(JDK)的一部分,我们可以使用许多工具,这些工具有助于更好地理解Java代码. 这些工具之一是javap命令,它为我们提供 ...

  8. 如何“反编译” Java类文件? [关闭]

    我可以使用什么程序来反编译类文件? 我实际上会得到Java代码,还是仅仅是JVM汇编代码? 关于该站点上的Java性能问题,我经常看到已经"反编译" Java类文件以查看编译器如何 ...

  9. IDEA java类文件左下角出现橙红色的圆形小标识

    IDEA java类文件左下角出现红色的J标识 是由于maven加载的时候没有把当前modual识别成一个modual加载 解决办法 方法一: 方法二:右键,选中标红java类所在包路径的java目录 ...

最新文章

  1. 如何使用Leangoo看板统计中的任务周期?
  2. 一网打尽数据结构中线性表链表的相关算法
  3. python前端学习-------Flask框架基础(建议收藏)
  4. linux 文件io实例代码,linux 文件IO(示例代码)
  5. 如何在React中使用功能组件
  6. Web Hacking 101 中文版 九、应用逻辑漏洞(三)
  7. Swift调用微信支付宝SDK(Swift4.0)
  8. Vivado 错误代码 [DRC PDCN-2721] 解决
  9. R语言也能玩ps?magick包你值得拥有
  10. 从html源码中获取图片链接地址和视频链接地址
  11. 2020计算机视觉会议地点,2020年计算机视觉与信息技术国际会议(CVIT 2020)
  12. L1-054 福到了(15 分)
  13. java 点击屏幕_JAVA+Appium 自动化实现手机屏幕滑动点击操作
  14. DNS劫持 DNS污染 介绍 与 公共DNS 推荐
  15. Laplace(拉普拉斯)平滑
  16. C语言中求余运算符的注意事项
  17. 九度OJ 1375 陈博的完美主义 (枚举,细心细心)
  18. Oracle GoldenGate心跳表检测延迟
  19. 基于 python pulp 库求解船舶泊位调度线性规划问题
  20. 我为什么不再玩手机游戏了

热门文章

  1. 设计模式(一):工厂方法
  2. vaadin教程_Vaadin教程
  3. jdk8 获取上一个月时间_JDK 10:FutureTask获取一个toString()
  4. javafx阴影_JavaFX技巧23:节省内存! 属性的阴影场
  5. pcl_openmap_OpenMap教程第2部分–使用MapHandler构建基本地图应用程序–第1部分
  6. jooq和jdbc_将jOOQ与JDBC比较
  7. hazelcast入门教程_Hazelcast入门指南第5部分
  8. java 解析日期格式_日期/时间格式/解析,Java 8样式
  9. jsr303 自定义消息_JSR 303从I18N属性文件加载消息
  10. JDK 14的征兆开始出现