使用ASM 4處理Java類文件–第二部分:Tree API

什么是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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/373231.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/373231.shtml
英文地址,請注明出處:http://en.pswp.cn/news/373231.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

php 去除 html 屬性,用PHP 去掉所有html標簽里的部分屬性

用PHP 去掉所有html標簽里的部分屬性http://zhidao.baidu.com/question/418471924.html用PHP 去掉所有html標簽里的部分屬性 tppabsset_time_limit(0);function view_dir($dir){$dpopendir($dir); //打開目錄句柄//echo "".$dir."";$path2;while ($file r…

在Windows上安裝Elasticsearch 5.0

在windows上安裝Elasticsearch Elasticsearch可以使用.zip軟件包安裝在Windows上。 elasticsearch-service.bat命令&#xff0c;它將設置Elasticsearch作為服務運行。 Elasticsearch的最新穩定版在Download Elasticsearch下載&#xff0c;其他的版本在Past Releases page下載。…

Java EE 6示例– Galleria

您是否一直想知道在哪里可以找到使用Java EE 6構建的良好端到端示例&#xff1f; 我有。 您在網上找到的大多數東西都是非常基礎的&#xff0c;不能解決現實世界中的問題。 Java EE 6教程就是這樣。 所有其他內容&#xff0c;例如Adam Bien所發表的大多數內容&#xff0c;都是范…

二維有限體積 matlab,二維有限體積法計算熱傳導及源碼.pdf

二維有限體積法計算熱傳導及源碼//#include "stdafx.h"#include #include #include #include #include using namespace std;#define q 500#define k 1000void main (){ //input the value you want:double L,dx,dy,T,Ax,Ay,d;int m,n,i,j,kk,mm ;//char str1[20];ch…

ubuntu與win10互換硬盤

實例&#xff1a;將sdb上的ubuntu轉移至sda&#xff0c;將sda上的win轉移至sdb1. 備份資料2. 制作老毛桃PE盤3. 格式化sda4. dd if/dev/sdb of/dev/sda ,將sdb克隆到sda上5. 利用Linux live cd修復grub2&#xff08;BIOS不會認GPT分區&#xff09; sudo grub-install /dev/sda …

如何在Jetty中使用SPDY

SPDY是Google提出的一種新協議&#xff0c;是針對網絡的新協議。 SPDY與HTTP兼容&#xff0c;但嘗試通過壓縮&#xff0c;多路復用和優先級降低網頁負載。準確地說&#xff0c;快速的目標是&#xff1a;&#xff08; http://dev.chromium.org/spdy/spdy-whitepaper &#xff09…

虐殺外星人java,逆天游戲《毀滅全人類2》登PS4 外星人瘋狂虐殺地球人

逆天游戲《毀滅全人類2》登PS4 外星人瘋狂虐殺地球人2016-10-17 10:45:58來源&#xff1a;游戲下載編輯&#xff1a;小年青評論(0)廣大的小伙伴都有看過許多外星人企圖入侵毀滅地球的電影&#xff0c;已此為題材而開發的游戲也不在少數。近日泛歐洲游戲信息組織又為一款該種題材…

電腦快捷鍵大全

最常用的快捷鍵F5------刷新 DELETE-----刪除 TAB----改變焦點CTRLC-----復制 CTRLX-----剪切 CTRLV----粘貼CTRLA-----全選 CTRLZ-----撤銷 CTRLS-----保存 ALTF4-----關閉 CTRLY-----恢復 ALTTAB-----切換CTRLF5---強制刷新…

ORM仇恨者無法理解

我看過無數的文章和評論&#xff08;尤其是評論&#xff09;&#xff0c;它們告訴我們ORM&#xff08;對象關系映射&#xff09;的概念有多糟糕&#xff0c;糟糕和錯誤。 以下是通常的聲明&#xff0c;以及我對它們的評論&#xff1a; “它們很慢” –映射有一些開銷&#xff0…

Android之仿微信圖片選擇器

先上效果圖。第一張圖顯示的是“相機”文件夾中的所有圖片&#xff1b;通過點擊多張圖片可以到第二張圖所示的效果&#xff08;被選擇的圖片會變暗&#xff0c;同時選擇按鈕變亮&#xff09;&#xff1b;點擊最下面的那一欄可以到第三張圖所示的效果&#xff08;顯示手機中所有…

oracle 快照用途,Oracle快照原理及實現總結

oracle數據庫的快照是一個表&#xff0c;它包含有對一個本地或遠程數據庫上一個或多個表或視圖的查詢的結果。也就是說快照根本的原理就是將本地或遠程數據庫上的一個查詢結果保存在一個表中。以下是建立的Snapshot&#xff0c;目的是從業務數據庫上將數據Copy到處理數據庫上&a…

loss function

什么是loss? loss: loss是我們用來對模型滿意程度的指標。loss設計的原則是&#xff1a;模型越好loss越低&#xff0c;模型越差loss越高&#xff0c;但也有過擬合的情況。   ??loss function: 在分類問題中&#xff0c;輸入樣本經過含權重矩陣θ的模型后會得出關于各個類別…

復雜的(事件)世界

這篇博客文章試圖總結CEP領域中的技術&#xff0c;并探討它們的主要功能和不足。 有時似乎過度使用了CEP一詞&#xff08;就像ESB一樣&#xff09;&#xff0c;下面的文章反映了我們對它的理解和理解。 ESPER&#xff08; http://esper.codehaus.org/ &#xff09;是流行的開源…

oracle查詢表的id,oracle 查看所有用戶及密碼 實現Oracle查詢用戶所有表

1、oracle 查看所有用戶及密碼SQL> select username from dba_users;2、 實現Oracle查詢用戶所有表下面為您介紹的語句用于實現Oracle查詢用戶所有表&#xff0c;如果您對oracle查詢方面感興趣的話&#xff0c;不妨一看。select * from all_tab_comments-- 查詢所有用戶的表…

php 字符串加密與解密

/** * param $data 需要加密的字符串 * param $key 加密的密碼 * return string 加密后的字符串 */function _encrypt($data, $key){ $key md5($key); $x 0; $len strlen($data); $l strlen($key); $char; $str; for ($i …

java如何從方法返回多個值

本文介紹三個方法&#xff0c;使java方法返回多個值。 方法1&#xff1a;使用集合類方法2&#xff1a;使用封裝對象方法3&#xff1a;使用引用傳遞示例代碼如下&#xff1a; import java.util.HashMap; import java.util.Map;public class Test {/*** 方法1&#xff1a;使用集合…

FindBugs和JSR-305

假設那組開發人員在大型項目的各個部分上并行工作–一些開發人員在進行服務實現&#xff0c;而其他開發人員在使用該服務的代碼。 考慮到API的假設&#xff0c;兩個小組都同意服務API&#xff0c;并開始單獨工作。 您認為這個故事會有幸福的結局嗎&#xff1f; 好吧&#xff0c…

java使用org.apache.poi讀取與保存EXCEL文件

一、讀EXCEL文件 1 package com.ruijie.wis.cloud.utils;2 3 import java.io.FileInputStream;4 import java.io.FileNotFoundException;5 import java.io.IOException;6 import java.io.InputStream;7 import java.text.DecimalFormat;8 import java.util.ArrayList;9 import …

oracle 指定格式化,Oracle中的格式化函數

格式化函數提供一套有效的工具用于把各種數據類型(日期/時間&#xff0c;int&#xff0c;float&#xff0c;numeric)轉換成格式化的字符串以及反過來從格式化的字符串轉換成原始的數據類型。表 5-6. 格式化函數函數返回描述例子to_char(datetime, text)text把datetime 轉換成 s…

彈性數組

看這個結構體的定義&#xff1a;typedef struct st_type{ int nCnt; int item[0];}type_a;&#xff08;有些編譯器會報錯無法編譯可以改成&#xff1a;&#xff09;typedef struct st_type{ int nCnt; int item[];}type_a; 這樣我們就可以定義一個可變長的結…