天天看點

[轉]ASM插入代碼 visitFieldInsn

今天介紹下asm3.0,開始之前先思考幾個問題:

1.asm是什麼? 2.asm 跟傳說中的aop三劍客apt、aspectj、javassit有什麼關系? 3.asm是怎樣修改class檔案的?

帶着問題開始今天的分享:

asm 是一個 java 位元組碼操控架構。它能被用來動态生成類或者增強既有類的功能。asm 可以直接産生二進制 class 檔案,也可以在類被加載入 java 虛拟機之前動态改變類行為。java class 被存儲在嚴格格式定義的 .class 檔案裡,這些類檔案擁有足夠的中繼資料來解析類中的所有元素:類名稱、方法、屬性以及 java 位元組碼(指令)。asm 從類檔案中讀入資訊後,能夠改變類行為,分析類資訊,甚至能夠根據使用者要求生成新類。說白了asm是直接通過位元組碼來修改class檔案。

分别解釋下這幾個名詞

apt:apt(annotation processing tool)即注解處理器,是一種處理注解的工具,确切的說它是javac的一個工具,它用來在編譯時掃描和處理注解。注解處理器以java代碼(或者編譯過的位元組碼)作為輸入,生成.java檔案作為輸出。簡單來說就是在編譯期,通過注解生成.java檔案 aspectj:aspectj是一個面向切面的架構,它擴充了java語言。aspectj定義了aop文法,是以它有一個專門的[編譯器]用來生成遵守java位元組編碼規範的class檔案。适合在某一個方法前後插入部分代碼,處理某些邏輯:比如方法運作時間、插入動态權限檢查等。問題會造成很多的備援代碼,産生很多代理類。簡單來說就是在生成class時動态織入代碼 javassit: javassist是一個開源的分析、編輯和建立java位元組碼的類庫。是由東京工業大學的數學和計算機科學系的 shigeru chiba(千葉滋)所建立的。簡單來說就是源碼級别的api去修改位元組碼
[轉]ASM插入代碼 visitFieldInsn

各種方式作用時機

開始這個問題之前我們先學習幾個東西。

位元組碼

通路者模式

一個稱為元素(element),另一個稱為通路者(visitor)。元素有一個accept方法,該方法接收通路者作為參數;accept()方法調用通路者的visit()方法,并且将元素自身作為參數傳遞給通路者。由元素本身決定是否通路

在asm中元素(被通路者)classreader、methodnode等等,通路者接口包含classvisitor、annotationvisitor、fieldvisitor、methodvisitor

下面我們先簡單實作一個插樁操作

有如下代碼:

我們要在sendmessage方法中添加一行代碼變為下列

1.我們這裡使用一個android studio的plugin (​<code>​asm bytecode outline​</code>​)檢視​<code>​main2activity​</code>​的asm代碼,看主要的​<code>​sendmessage​</code>​部分

看起來很懵逼,其實這裡隻不過是asm幫助我們調用java bytecode罷了。

修改你想要的代碼,同樣使用<code>asm bytecode outline</code>plugin對比差異代碼

看的出我們在​<code>​mv.visitfieldinsn(putfield, "android/os/message", "what", "i");​</code>​後邊插入了三行代碼(其中的label以及行數設定可以不用理),那麼這三行代碼什麼意思呢?這裡就用到了上邊提到的java bytecode知識,意思是:将變量2,1分别入棧,并将2變量指派給message的obj。

好了那麼我們開始寫asm,這裡我們使用android transform api作為前置條件掃描檔案。(有關于transform api後期分享)

開始之前先解釋幾個類:

1.opcodes接口定義了一些常量,尤其是版本号,通路标示符,位元組碼等資訊; 2.classreader用于讀取class檔案,主要用于class檔案的分析,可接受一個classvisitor;classreader會将解析過程中産生的類的部分資訊,比如通路辨別符,字段,方法逐個送入classvisitor,後者在接收到對應的資訊後,進行各自的處理; 3.classvisitor的子類classwriter: 負責進行class檔案的輸出和生成。classvisitor在進行字段和方法處理的時候,會委托給fieldvistor和methodvisitor進行處理;在類的處理過程中,會建立對應的fieldvisitor和methodvisitor對象;fieldvisitor和methodvisitor類也各自有1個重要的子類,fieldwriter和methodwriter;當classwriter進行字段和方法的處理時,也是依賴這兩個類進行的; 4.classvisitor,fieldvisitor,methodvisitor都可以使用委托的方式,将實際的處理工作交給内部的委托類進行;它們内部有一些列的visitxxx方法,這些方法就是asm 的實際方法code。

建立元素跟通路者

這裡的通路者​<code>​scanclassvisitor​</code>​繼承了​<code>​classvisitor​</code>​,這裡我們要修改某一個方法,是以實作了​<code>​visitmethod​</code>​方法,并篩選其中的​<code>​sendmessage​</code>​方法

上邊的​<code>​scanmethodvisitor​</code>​是實作了​<code>​methodvisitor​</code>​通路者,看asm代碼,我們需要在​<code>​mv.visitfieldinsn(putfield, "android/os/message", "what", "i");​</code>​行後插入代碼,是以需要實作​<code>​visitfieldinsn​</code>​方法

opcode: putfield owner: "android/os/message" name:"what" desc:"i"

好了接下來運作代碼transform看效果

總結:asm直接修改class檔案确實效率很高,但因直接操作位元組碼,需要有位元組碼知識,不适合直接上手,相比較來​<code>​javassit​</code>​源碼級修改class檔案更友善些。

demo

作者:heiheiwanne