字節碼動態注入

在Android開發中,咱們常常經過Gradle Plugin配合Android Gradle Plugin提供的Tranform API,並應用Javassit字節碼編輯庫在Android打包過程當中作一些特殊操做。例如:自動埋點,熱修復等。java

Javassit提供了一個方便獲取ClassPool的方法,ClassPool.getDefault(),它是個單列對象。linux

當你在使用assemble命令打包你的Android應用時,默認會執行assembleDebug和assembleRelease,若是你增長了定義的buildTypes或者flavors,全部的assembleXXX命令都會執行。所以assemble屢次調用void transform(TransformInvocation transformInvocation)方法。bash

此時,若是你是使用ClassPool.getDefault()來存放你須要操做的Class,而且在自定義Transform中對CtClass應用writeFile(),toClass()或者toByteCode()方法將其轉換成Class文件,那麼Javassist就會凍結(frozen)這個CtClass對象,以後就不能修改這個CtClass對象了。因此transform方法第二次執行時,咱們在對ClassPool.getDefault()裏面的CtClass作writeFile(),toClass()或者toByteCode()操做就會發生xxx class is frozen.的錯誤。oracle

Javassit的此異常是爲了警告開發者不要修改已經被JVM加載的class文件,由於JVM不容許從新加載一個類。app

解決方法:不要使用ClassPool.getDefault()來獲取ClassPool,經過ClassPool classPool = new ClassPool(true)的方式本身建立,由於每次都是新建立的ClassPool,因此在執行assemble後屢次調用void transform(TransformInvocation transformInvocation)方法不會出現上述異常。less

1.環境 mac,若是是Linux更方便 2.Maven 3.1.0 jdk1.6 額外修改maven配置文件 JDK是向上兼容。 好比JDK1.8就兼容1.5 1.6 1.7maven

1.java探針 java探針分爲動態注入和靜態注入ui

須要瞭解 :linux.net

實例1:給文件建立軟連接 命令:ln -s log2013.log link2013 說明:爲log2013.log文件建立軟連接link2013,若是log2013.log丟失,link2013將失效3d

具體用法是:ln -s 源文件 目標文件。 (link)它就能夠,沒必要重複的佔用磁盤空間。例如:ln -s /bin/less /usr/local/bin/less

Mac OS X安裝JDK1.6及相關解決找不到tools.jar的問題 1.安裝JDK1.6

oracle官網從jdk1.7開始纔有Mac版的安裝包,但有的項目必須使用jdk1.6,因此必須從其餘途徑安裝jdk1.6了。

2.包路徑等問題

系統默認安裝的JRE路徑  /System/Library/Frameworks/JavaVM.framework/

oracle和apple等安裝的JDK包的路徑 /Library/Java/JavaVirtualMachines/

3.JAVA_HOME在哪了? /Library/Java/JavaVirtualMachines/1.6.0_38-b04-436.jdk/Contents/Home

注:1.6.0_38-b04-436.jdk目錄名字與安裝的jdk版本有關

4.rt.jar、jsse.jar、tools.jar去哪了? rt.jar和tools.jar已經集成到/Library/Java/JavaVirtualMachines/1.6.0_38-b04-436.jdk/Contents/Classes/classes.jar

jsse.jar也在Classes目錄下

建議把classes.jar和jsse.jar創建軟鏈接到/Library/Java/JavaVirtualMachines/1.6.0_38-b04-436.jdk/Contents/Home/lib/下,而且classes.jar的軟連接命名爲rt.jar。同理,也建多一個爲tools.jar的軟連接。

這樣就能夠避免一些時候會發生找不到rt.jar、tools.jar的問題了。

  1. 全局查找tools.jar find / -name tools.jar  2. 例如:

#進到lib目錄 cd /Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home/lib

#創建軟鏈接 ln -s /Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home/bundle/Classes/classes.jar tools.jar

查找這個類的根本解決辦法是我用到了com.sun.tools.attach 包下的VirtualMachine類,根據這個類所在的jar包找到了classes.jar包 如圖:

3.配置文件: 只須要在/etc/bashrc或者/etc/profile下添加配置

5.配置JAVA_HOME Mac OS X的環境變量文件在/etc/profile,unix一向重要的文件。 在此添加最下端添加

 export JAVA_HOME=/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home export PATH=$PATH:$JAVA_HOME/bin 保存後退出,而後註銷就生效了。

注:添加JAVA_HOME後,系統也會使用你配置的JAVA_HOME的jdk爲默認JDK。

  1. instrutment中retransformClasses和redefineClasses

總結: class文件隨着虛擬機啓動的時候,會通過premain方法,premain方法中定義了transform,這個premain在虛擬機啓動的時候會被執行一次,而後經過transform方法對類進行了修飾,就好像被穿了一件衣服,class加載的時候注入了transform中的內容,之後每次class執行的時候就會走一次transform裏面的東西,就好下你給以前給你穿了一個衣服,之後每次看你你都穿着這個衣服。

retransformClasses是由於agent雖然嵌入了,可是在虛擬機啓動的時候,某些類好比thread,在javaagent啓動以前就已經加載到了內存,javaagent也是類,在javaagent加載以前虛擬機須要加載一些必須的類來保證個人javaagent的運行,好比說thread,這個時候thread就沒有被」穿上衣服「,即沒有被transform修飾,也就不能被javaagent監控到,這個時候就須要retransformClasses從新加載,注意retransformClasses會讓沒有被」穿上衣服的類」穿上衣服「

redefineClasses也是從新加載一次,可是這裏注意並無給類」穿衣服「,即經過這種方法加載的時候,類不會通過transform方法。這個方法的做用相似於,原來給類」穿上了衣服「,經過這個方法能夠給這個類」脫了衣服「

一個關於redefineClass的不錯的博文:https://blog.csdn.net/raintungli/article/details/51655608

總的來講他們兩個的共同點都是讓類(class文件)從新加載進入內存,不一樣的是前者是爲了「穿衣服」,後者是爲了「脫衣服」

訪問者模式:https://www.jianshu.com/p/1f1049d0a0f4

相關文章
相關標籤/搜索