字節(jié)碼插樁簡析
? ? ? ? 我們都知道java文件需要編譯成class字節(jié)碼文件才能被jvm加載執(zhí)行,這里我們順便說一下jvm加載一個class類的流程,如圖:

驗證:即是驗證class文件內(nèi)容的有效性,可能你會說都是java的編譯器javac干的咋還可能會有錯呢,這就是和我們今天的主角有關(guān)系了

準備:對類的靜態(tài)變量賦初值,這里不包括final修飾的靜態(tài)變量,因為它是在編譯階段就賦值了,另外final修飾的靜態(tài)變量引用不會引起類加載,同時它的賦初值并不是你在類里面給的值,例如 static int i = 2,在準備階段賦的值是0
解析:將常量池內(nèi)的符合引用轉(zhuǎn)化為直接引用,不理解可以不必深究,了解即可
初始化:對類的所有變量進行初始化,同時將上面準備階段已經(jīng)初始化過的靜態(tài)變量賦所需要的值,另外類的初始化會生成一個類對象
使用:這個我不會
卸載:類的卸載條件比較苛刻,需要class對象及其classLoader對象(即加載這個class類的類加載器)同時釋放才能對其進行垃圾回收,所以在java8之前加載的class對象會被放在稱為永久代的方法區(qū)中,從這里看來類對象也不是越多越好,所以開發(fā)的時候就別一個字段就搞一個類了
? ? ? ?介紹了這么多,我們現(xiàn)在進入正題,首先我們先來說一下剛開始的一個問題,既然class是jdk編譯的,那么為啥還需要檢查呢,這是因為編譯之后的class文件是可以被修改的,在java中要修改class需要兩種技術(shù),分別是javaAgent和javassist,第一個負責在class被classLoader加載之前對classLoader進行攔截,第二個是負責將class的二進制文件進行修改,具體怎么修改的細節(jié)不用關(guān)注,怕你會了公司請不起你:
? ? ? ? ?說完了javaAgent的運行流程,我們再來說說在哪些場景需要用到它,第一個主要場景是在面試中,可以反問面試官知不知道agent就敢來面試你,第二個主要場景是在寫服務鏈路監(jiān)控相關(guān)的,也可以簡單點理解為埋點,但是這種方式是動態(tài)織入的,意思就是說不需要在業(yè)務代碼中加入這些埋點邏輯,你可以通過javaagent的方式去attah你的業(yè)務服務,這種方式的好處是對業(yè)務無侵入,是一種邊車設(shè)計模式,能夠?qū)崿F(xiàn)基礎(chǔ)設(shè)施下沉,降低微服務的復雜度。
? ? ? 最后我們來簡單說一下javaAgent如何使用:
? ? ? ? ?
1、編寫agent方法
public class MyAgent {
public static void premain(String args, Instrumentation instrumentation) throws Exception {
System.out.println("Hello javaagent permain:"+args);
}
}
2、添加premain-class參數(shù)
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.2</version>
<configuration>
<archive>
<manifestEntries>
<Project-name>${project.name}</Project-name>
<Project-version>${project.version}</Project-version>
<Premain-Class>com.javaagent.MyAgent</Premain-Class>
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<Can-Retransform-Classes>true</Can-Retransform-Classes>
</manifestEntries>
</archive>
<skip>true</skip>
</configuration>
</plugin>
3、構(gòu)建打包

4、在任一JAVA應用中 添加jvm 參數(shù)并啟動 -javaagent:xxx.jarjavaagent META-INF/MANIFEST.MF
參數(shù)說明:
Premain-Class:必填,agent啟動
classCan-Redefine-Classes:默認為false ,是否允許重新定義
classCan-Retransform-Classes:默認為false,是否允許重置Class,重置后相當于class 從classLoade中清除,下次有需要的時候會重新裝載,也會重新走Transformer 流程。
Boot-Class-Path:agent 所依賴的jar 路徑,多個用空格分割
創(chuàng)建一個測試類MyAgentTest并運行查看結(jié)果
public class MyAgentTest {
public static void main(String[] args) {
System.out.println("main");
}
}
//運行結(jié)果:main
添加jvm參數(shù)
參數(shù)內(nèi)容:-javaagent:/Users/jinyunlong/IdeaProjects/test-agent/target/test-agent-1.0-SNAPSHOT.jar=123

再次運行測試類MyAgentTest并查看結(jié)果
總結(jié):
優(yōu)點:javaAgent其實本質(zhì)上就是改變class文件,更準確的應該說是動態(tài)改變class中的方法,對其進行增強,比如說統(tǒng)一埋點,打日志或者其它業(yè)務功能,能夠讓這些基礎(chǔ)功能和業(yè)務解耦,實現(xiàn)基礎(chǔ)設(shè)施下沉,同時還能保證較高的準確度
缺點:部署比較麻煩,其次通過這種方式對類的方法進行增強,或多或少會影響服務的性能,這要根據(jù)你的業(yè)務流量來判斷是否需要接入
