sponsored links

Java高階用法,寫個代理侵入你 ?

小王是一個剛來不久的妹子,啊呸,是一個剛來不久的程式媛,經常垂頭喪氣的~讓我很是不解,終於有一天我怕小王哪天想不開離職了豈不是會增加我的工作量(部門為數不多的妹子 - 1)?於是乎,我主動找小王進行了談心找到了問題所在,原來是小王程式設計經驗不足,不知道如何巧妙的進行日誌列印,那麼因果關係就總結出來了:經驗不足導致編碼經常出錯,編碼出錯由於日誌未列印導致排查困難,排查困難導致開發抑鬱。查到問題的原因,那麼進行對症下藥即可~

其實以上問題我相信很多小夥伴都遇到過,開發過程中未出現的錯誤在上線後就頻頻出現,那麼只能不斷的進行新增日誌列印然後再打包上傳進行問題跟蹤,一天的時間絕大部分都浪費在了打包上傳的上面。那麼能不能直接進行bug跟蹤,然後檢視到問題出錯的所在?這種需求不亞於給奔跑中的汽車更換輪胎,匪夷所思卻又無可奈何~其實有開發經驗的小夥伴已經想出來一箇中間件,那就是 Arthas!但是這篇文章不是介紹如何使用 Archas,而是我們自己能不能實現這種動態除錯的技能?那麼就進入我們今天的整體 --- Java Agent 技術

Java Instrument

這個玩意並不是什麼 Java 的新特性,早在 JDK 1.5 的時候就誕生了,位於 java.lang.instrument.Instrumentation 中,它的作用就是用來在執行的時候重新載入某個類的 calss 檔案的 api。

這種類的實現方式其實是一種 Java Agent 技術,我們這裡可以順帶了解一下什麼是 Java Agent。

一、Java Agent

代理這個詞對於我們開發人員來說並不預設,我們經常用到的 AOP 面向切面程式設計用到的就是代理方式。它可以動態切入某個面,進行程式碼增強 。這種不用重複補充輪子的方式大大增加了我們開發效率,那麼這裡捕獲到了一個關鍵詞 動態。那麼 Java Agent 如何實現?那就可以說到 JVMTI(JVM Tool Interface) ,這是Java 虛擬機器對外提供的 Native 程式設計介面,透過它我們可以獲取執行時JVM的諸多資訊,而 Agent 是一個執行在目標 JVM 的特定程式,它可以從目標 JVM 獲取資料,然後將資料傳遞給外部程序,然後外部程序可以根據獲取到的資料進行動態Enhance。

Java高階用法,寫個代理侵入你 ?

那麼 Java Agent 什麼時候能夠載入?

  • 目標 JVM 啟動時
  • 目標 JVM 執行時

那麼我們關注的是 執行時 ,這樣子就能滿足我們動態載入的需求。

而 Java Agent看上去這麼高大上,我們要如何編寫?當然在 JDK 1.5 之前,實現起來是具有困難性的,我們需要編寫 Native 程式碼來實現,那麼 JDK 1.5 之後我們就可以利用上面說到的 Java Instrument 來實現了!

首先我們先了解一下 Instrumentation 這個介面,其中有幾個方法:

  • addTransformer(ClassFileTransformer transformer, boolean canRetransform)

加入一個轉換器 Transformer ,之後所有的目標類載入都會被 Transformer 攔截,可自定義實現 ClassFileTransformer 介面,重寫該介面的唯一方法 transform() 方法,返回值是轉換後的類位元組碼檔案

  • retransformClasses(Class<?>... classes)

對 JVM 已經載入的類重新觸發類載入,使用上面自定義的轉換器進行處理。該方法可以修改方法體,常量池和屬性值,但不能新增、刪除、重新命名屬性或方法,也不能修改方法的簽名

  • redefineClasses(ClassDefinition... definitions)

此方法用於替換類的定義,而不引用現有類檔案位元組。

  • getObjectSize(Object objectToSize)

獲取一個物件的大小

  • appendToBootstrapClassLoaderSearch(JarFile jarfile)

將一個 jar 檔案新增到 bootstrap classload 的 classPath 中

  • getAllLoadedClasses()

獲取當前被 JVM 載入的所有類物件

redefineClasses 和 retransformClasses 補充說明

兩者區別:

redefineClasses 是自己提供位元組碼檔案替換掉已存在的 class 檔案

retransformClasses 是在已存在的位元組碼檔案上修改後再進行替換

替換後生效的時機

如果一個被修改的方法已經在棧幀中存在,則棧幀中的方法會繼續使用舊位元組碼執行,新位元組碼會在新棧幀中執行

注意點

兩個方法都是隻能改變類的方法體、常量池和屬性值,但不能新增、刪除、重新命名屬性或方法,也不能修改方法的簽名

二、實現 Agent

1、編寫方法

上面我們已經說到了有兩處地方可以進行 Java Agent 的載入,分別是 目標JVM啟動時載入 和 目標JVM執行時載入,這兩種不同的載入模式使用不同的入口函式:

1、JVM 啟動時載入

入口函式如下所示:

 // 函式1
public static void premain(String agentArgs, Instrumentation inst);
// 函式2
public static void premain(String agentArgs);

JVM 首先尋找函式1,如果沒有發現函式1,則會尋找函式2

2、JVM 執行時載入

入口函式如下所示:

// 函式1
public static void agentmain(String agentArgs, Instrumentation inst);
// 函式2
public static void agentmain(String agentArgs);

與上述一致,JVM 首先尋找函式1,如果沒有發現函式1,則會尋找函式2

這兩組方法的第一個引數 agentArgs 是隨同 “-javaagent” 一起傳入的程式引數,如果這個字串代表了多個引數,就需要自己解析這引數,inst 是 Instrumentation 型別的物件,是 JVM 自己傳入的,我們可以那這個引數進行引數的增強操作。

Java高階用法,寫個代理侵入你 ?

2、宣告方法

當定義完這兩組方法後,要使之生效還需要手動宣告,宣告方式有兩種:

1、使用 MANIFEST.MF 檔案

我們需要建立resources/META-INF.MANIFEST.MF 檔案,當 jar包打包時將檔案一併打包,檔案內容如下:

Manifest-Version: 1.0
Can-Redefine-Classes: true            # true表示能重定義此代理所需的類,預設值為 false(可選)
Can-Retransform-Classes: true          # true 表示能重轉換此代理所需的類,預設值為 false (可選)
Premain-Class: cbuc.life.agent.MainAgentDemo         #premain方法所在類的位置
Agentmain-Class: cbuc.life.agent.MainAgentDemo         #agentmain方法所在類的位置

2、如果是maven專案,在pom.xml加入

Java高階用法,寫個代理侵入你 ?

3、指定 agent

要讓目標JVM認你這個 Agent ,你就要給目標JVM介紹這個 Agent

1、JVM 啟動時載入

我們直接在 JVM 啟動引數中加入 -javaagent 引數並指定 jar 檔案的位置

# 將該類編譯成 class 檔案
javac TargetJvm.java
# 指定agent程式並執行該類
java -javaagent:./java-agent.jar TargetJvm

2、JVM 執行時載入

要實現動態除錯,我們就不能將目標JVM停機後再重新啟動,這不符合我們的初衷,因此我們可以使用 JDK 的 Attach Api 來實現執行時掛載 Agent。

Attach Api 是 SUN 公司提供的一套擴充套件 API,用來向目標 JVM 附著(attach)在目標程式上,有了它我們可以很方便地監控一個 JVM。Attach Api 對應的程式碼位於 com.sun.tools.attach包下,提供的功能也非常簡單:

  • 列出當前所有的 JVM 例項描述
  • Attach 到其中一個 JVM 上,建立通訊管道
  • 讓目標JVM載入Agent

該包下有一個類 VirtualMachine,它提供了兩個重要的方法:

  • VirtualMachine attach(String var0)

傳遞一個程序號,返回目標 JVM 程序的 vm 物件,該方法是 JVM程序之間指令傳遞的橋樑,底層是透過 socket 進行通訊

  • void loadAgent(String var1)

該方法允許我們將 agent 對應的 jar 檔案地址作為引數傳遞給目標 JVM,目標 JVM 收到該命令後會載入這個 Agent

有了 Attach Api ,我們就可以建立一個java程序,用它attach到對應的jvm,並載入agent。

以下是簡單的 Attach 程式碼實現:

Java高階用法,寫個代理侵入你 ?

注意:在mac上安裝了的jdk是能直接找到 VirtualMachine 類的,但是在windows中安裝的jdk無法找到,如果你遇到這種情況,請手動將你jdk安裝目錄下:lib目錄中的tools.jar新增進當前工程的Libraries中。

上面程式碼十分簡易的實現了 Attach 的方式,透過尋找當前系統中所有執行的 JVM 程序,然後透過比對 PID 來篩選出目標JVM,然後讓 Agent 附著在目標 JVM 上。當然這邊已經簡易到直接在程式碼中指定目標JVM的 PID,這種方式在實際生產中是十分不可取的,我們可以透過動態引數的方式傳入 PID~!而 Attach 的執行原理也不復雜,簡單流程如下:

Java高階用法,寫個代理侵入你 ?

三、案例說明

我們上述簡單聊了下 Java Agent 的實現過程,那我們下面也簡單寫個案例來理解一下 Java Agent 的實現過程~

Java高階用法,寫個代理侵入你 ?

我們上面說到可以使用 Java Instrumentation 來完成動態類修改的功能,並且在 Instrumentation 介面中我們可以透過 addTransformer() 方法來增加一個類轉換器,類轉換器由類 ClassFileTransformer 介面實現。該介面中有一個唯一的方法 transform() 用於實現類的轉換,也就是我們可以增強類處理的地方!當類被載入的時候就會呼叫 transform()方法,實現對類載入的事件進行攔截並返回轉換後新的位元組碼,透過 redefineClasses()或retransformClasses()都可以觸發類的重新載入事件。

實際操作

1)準備目標JVM

我們這裡直接使用一個 SpringBoot 專案來試驗,方便大家增強改造~ 專案結構如下:

target-jvm
    ├─src
       ├─main
          ├─java
             └─cbuc
                 └─life
                     └─targetjvm
                         ├─controller
                         |     └─TestController.java
                         └─service
                         |     └─SimpleService.java
                         └─TargetJvmApplication.java

其中 TestController 和 SimpleService 兩個類的內容也很簡單,直接貼程式碼

Java高階用法,寫個代理侵入你 ?

Java高階用法,寫個代理侵入你 ?

2)準備 Agent

1、編寫方法

然後編寫我們的Agent jar包。因為懶惰,所以我這邊將 premain 和 agentmain 兩個方法寫在同一個 jar 包中,然後分別以 啟動時 和 執行時 來模擬場景~

Java高階用法,寫個代理侵入你 ?

很簡單,一個類中包含了我們需要的所有功能~ 防止圖片內容過於擁擠,小菜貼心地分別粘貼出核心程式碼:

  • premain

Java高階用法,寫個代理侵入你 ?

  • agentmain

Java高階用法,寫個代理侵入你 ?

  • ClassFileTransformer

2)宣告方法

然後將 Agent 打包,打包的時候需要在 pom.xml 檔案中新增以下內容

然後執行mvn assembly:assembly 既可

3)啟動 Agent

當我們已經準備好了兩個 jar 包便可以開始測試了!

1、啟動時載入

nohup java -javaagent:./java-agent-jar-with-dependencies.jar -jar target-jvm.jar &
xxxxxxxxxxbr nohup java -javaagent:./java-agent-jar-with-dependencies.jar -jar target-jvm.jar &

我們直接啟動時新增引數,帶上我們的 Agent jar包

結果並沒有讓小菜太尷尬,成功的實現我們想要的功能,但是這只是啟動時載入,明顯不是我們想要的~ 我們來試下執行時如何載入

2、執行時載入

正常執行下,方法並沒有做耗時統計,我們的需求就來了,我們想要統計該方法的耗時,首先獲取該程序ID

然後透過 Attach 方式(呼叫controller 的 active() 方法)附著 Agent,我們可以實時檢視控制檯

已經可以看到 Agent 似乎已經成功附著了,然後我們繼續請求 test 介面

可以發現 resolve 方法已經被我們增強了!

四、題外話

上面我們已經簡單的實現了動態操作目標類檔案,文章開頭就說明了給奔跑中的汽車更換輪胎是一個匪夷所思卻又無可奈何的需求,但是這個需求能不能讓別人實現,其實是可以的,而這個就是小菜的主要目的,我們瞭解瞭如何實現動態換輪胎的原理後,當我們運用其成熟的中介軟體也能更加應手而不會不知所措,知識不能讓我們只學會臥槽兩個字,而是當別人實現的時候我們能默默思考,思考後再說出牛逼~!感興趣的同學不妨拉取一下原始碼演練一番:Arthas gitee,已經使用過類似 Arthas 或 BTrace 的同學,看完相信會更加了解其工作執行原理,沒使用過的同學下次用到的時候也不會那麼戰戰兢兢!

分類: 體育
時間: 2021-12-05

相關文章

奢華智慧腕錶耀世而來: TAG HEUER泰格豪雅CONNECTED智慧腕錶耀黑版

奢華智慧腕錶耀世而來: TAG HEUER泰格豪雅CONNECTED智慧腕錶耀黑版
瑞士奢華製表品牌TAG Heuer泰格豪雅精心甄選上乘材質,同時融合經典優雅的黑色與時尚前衛的金色,傾力打造出一款引領潮流風尚的特別版智慧腕錶:泰格豪雅Connected智慧腕錶耀黑版.同時,新款腕錶 ...

濟南孚卡悅聽寶馬6系GT汽車音響改裝史泰格寶馬專車專用喇叭

濟南孚卡悅聽寶馬6系GT汽車音響改裝史泰格寶馬專車專用喇叭
是不是愛聽音樂,最主要還是看音樂好聽與否,這臺寶馬6系GT的原車音響配置雖然相比普通車型要好很多,但車主對音質的要求較高,原車音響表現顯然無法滿足車主的聽覺需求,為了打造一套高品質的音響系統,車主在網 ...

比亞迪e平臺3.0釋出 Ocean-X概念車首次亮相

比亞迪e平臺3.0釋出 Ocean-X概念車首次亮相
比亞迪在今年4月份上海車展上推出了全新的e-Platform 3.0,該平臺將用於下一代電動汽車,這也是比亞迪在電動汽車領域十多年經驗的結晶. 比亞迪e-Platform 3.0平臺已經逐漸進入中國市 ...

體視界・陝耀 | 楊倩全運會兩金一銅收官;地擲球首次亮相全運會精彩紛呈

體視界・陝耀 | 楊倩全運會兩金一銅收官;地擲球首次亮相全運會精彩紛呈
瞰全運 楊倩全運會兩金一銅收官 王芝琳(左)和楊倩在比賽後握手致意(新華社) 18日,全運會女子10米氣步槍團體決賽在長安常寧生態體育訓練比賽基地進行.最終,楊倩領銜的浙江隊(楊倩/王芝琳/韓佳予)以 ...

和保時捷Macan競爭,2022款瑪莎拉蒂Grecale將於11月16日首次亮相

和保時捷Macan競爭,2022款瑪莎拉蒂Grecale將於11月16日首次亮相
[新車迷]據悉,2022款瑪莎拉蒂Grecale將於11月16日首次亮相.瑪莎拉蒂Grecale是最受期待的高階SUV車型之一,將於11月16日在義大利米蘭首發,這家義大利公司今天證實了這一點.這將是 ...

殲-16D、無偵-7將首次亮相中國航展——中國空軍新聞發言人介紹空軍參加第13屆中國航展有關情況

殲-16D、無偵-7將首次亮相中國航展——中國空軍新聞發言人介紹空軍參加第13屆中國航展有關情況
新華社北京9月24日電 題:殲-16D.無偵-7將首次亮相中國航展--中國空軍新聞發言人介紹空軍參加第13屆中國航展有關情況 新華社記者劉濟美 空軍殲-20飛機開展飛行訓練(資料照片).新華社發(楊軍 ...

豐田亞洲龍音響改裝史泰格SE650C兩分頻 整套系統無損安裝,效果佳

豐田亞洲龍音響改裝史泰格SE650C兩分頻 整套系統無損安裝,效果佳
豐田亞洲龍的原車音響系統,表現一般,原車喇叭功率較小,聲音層次感較低,喇叭低頻較悶,中頻欠缺.原車的音響系統播放出來的音樂品質並不是特別的理想,並不能達到車主的聽音需求,於是,車主經過朋友介紹來到了西 ...

德國藝術偽造者Wolfgang Beltracchi首次亮相NFT、推出Greats系列

德國藝術偽造者Wolfgang Beltracchi首次亮相NFT、推出Greats系列
Beltracchi 目前正在創作 4,608 種不同藝術風格的列奧納多·達·芬奇的<救世主>. 當 Beltracchi 被捕時,他被抓到偽造著名藝術家的作品,包括 Max Ernst. ...

造型邪惡!美國“戰鬥機器狗”首次亮相,背上馱著自動步槍

造型邪惡!美國“戰鬥機器狗”首次亮相,背上馱著自動步槍
[環球網報道 記者 徐璐明]在近日舉辦的美國陸軍協會年會上,一款配備了步槍武器的機器狗首次亮相. 美國"動力"網站"戰區"專欄10月12日報道稱,美國" ...

寶馬325i音響改裝優特聲402+史泰格中低音,全套升級聽感更震撼

寶馬325i音響改裝優特聲402+史泰格中低音,全套升級聽感更震撼
每個人對音樂的感覺不同,喜好程度也不一樣,所以耳朵挑剔的人,是受不了劣質的汽車音響發出"噪音"的.總體來說原裝音響還是存在很多問題的,為此車主找到汕頭貓王改裝團隊,進行全套無損音響 ...

新車 | 約合人民幣10.9萬起,鈴木英格尼斯特別版釋出,提供2種配色

新車 | 約合人民幣10.9萬起,鈴木英格尼斯特別版釋出,提供2種配色
文:懂車帝原創 史景旭 [懂車帝原創 產品] 日前,鈴木正式釋出了旗下英格尼斯(Ignis)Red & White特別版車型.作為特別版本,新車採用專屬外觀及內飾配色.據悉,該車售價為1452 ...

新車 |“黑武士”酷炫登場,黑標MINI特別版即將亮相2021天津車展

新車 |“黑武士”酷炫登場,黑標MINI特別版即將亮相2021天津車展
文:懂車帝原創 李德喆 黑標MINI特別版將亮相津門 [懂車帝原創 行業] 全面"黑化"的MINI再度來襲.繼今年8月末在中國推出黑標特別版車型之後,黑標MINI COOPER家族 ...

宣泰戰役粟裕首次殲滅美械王牌師,毛主席詢問:83師被消滅多少?

宣泰戰役粟裕首次殲滅美械王牌師,毛主席詢問:83師被消滅多少?
全面內戰突然爆發,國民黨46萬大軍直撲華中解放區 1946年6月中旬,蔣介石為首的國民黨統帥部已經基本完成了全面內戰的部署,國民黨方面的主要戰略計劃是:迅速攻佔和控制共產黨各解放區重要城市和交通線.並 ...

新款大通V90房車,全新佈局首次亮相,全屋定製家居看著特洋氣

新款大通V90房車,全新佈局首次亮相,全屋定製家居看著特洋氣
Hello,大家好! 在很多人印象中,房車的內部就像自己的小家一樣,應該根據每個人的喜好需求來定製,但房車畢竟是需要走量的商品,想要實現完全私人化定製並不太現實.然而前兩天,我們在房車展會上就看到了一 ...

施羅德將穿71號球衣,凱爾特人隊史首次亮相

施羅德將穿71號球衣,凱爾特人隊史首次亮相
經過凱爾特人球迷投票後,丹尼斯-施羅德將在新賽季穿上71號球衣,這將是凱爾特人歷史上第一次有人穿上71號球衣. 施羅德上賽季穿17號球衣,但17號屬於約翰-哈弗裡切克,已經在凱爾特人退役.

「山東新聞聯播」剛出生就一米七?齊河動物王國7位長頸鹿“小朋友”首次亮相
國慶假期,泉城歐樂堡動物王國"萌寵幼兒園"又添了新的小寶貝.今年,白獅,長頸鹿,黑熊,蜘蛛猴等小動物都迎來新生,萌寶們為節日增添了許多歡樂的氣息.詳情請關注<山東新聞聯播&g ...

國產首款ADV風格邊三輪 銀鋼500大排量侉子首次亮相

國產首款ADV風格邊三輪 銀鋼500大排量侉子首次亮相
國產邊三輪摩托車的圈子當中,已經很久沒有出現過新的大排量車型,但是幾個月之後重慶老牌邊三輪廠家,銀鋼會帶來一款全新的大排量邊三輪.銀鋼的這款500cc級別的侉子,早在幾個月前就已經出現在工信部的新車公 ...

將於年底在英國首次交付,菲亞特釋出500X特別版

將於年底在英國首次交付,菲亞特釋出500X特別版
菲亞特幾個月前釋出了新的500X Dolcevita,現在,這款配備軟頂的SUV可以在英國市場訂購.定價從23,975英鎊起,預計將於2021年底在英國首次交付. 500X Dolcevita有與普通 ...

新車|配四出排氣!雷克薩斯RX特別版實車亮相,競爭林肯飛行家

新車|配四出排氣!雷克薩斯RX特別版實車亮相,競爭林肯飛行家
文:懂車帝原創 史景旭 [懂車帝原創 產品] 此前曾有訊息顯示,雷克薩斯旗下中大型SUV RX即將迎來換代.不過,在該車換代前,雷克薩斯又基於現款RX推出RX 450h MODELLISTA套件版,目 ...

彙集近40家制表品牌,“鐘錶與奇蹟”2022年將回歸日內瓦

彙集近40家制表品牌,“鐘錶與奇蹟”2022年將回歸日內瓦
近日,紅星新聞記者從瑞士高階製表基金會官方獲悉:2022年日內瓦"鐘錶與奇蹟"高階鐘錶展(以下簡稱:2022年"鐘錶與奇蹟"高階鐘錶展)將回歸日內瓦展覽中心(P ...