Java Agent
在java代碼開發過程中,有些測試環境的bug,通過java? jdwp(?Java Debug Wire Protocol)進行遠程調試。jdwp定義了調試器(debugger)和被調試的 Java 虛擬機(target vm)之間的通信協議。
Target vm 中運行著我們希望要調試的程序,它與一般運行的 Java 虛擬機沒有什么區別,只是在啟動時加載了 Agent JDWP 從而具備了調試功能。而 debugger 就是我們熟知的調試器,它向運行中的 target vm 發送命令來獲取 target vm 運行時的狀態和控制 Java 程序的執行。Debugger 和 target vm 分別在各自的進程中運行,他們之間的通信協議就是 JDWP。
JDWP 與其他許多協議不同,它僅僅定義了數據傳輸的格式,但并沒有指定具體的傳輸方式。這就意味著一個 JDWP 的實現可以不需要做任何修改就正常工作在不同的傳輸方式上
Target vm 端,JDWP 模塊必須以 Agent library 的形式在 Java 虛擬機啟動時加載,并且它必須通過 Java 虛擬機提供的 JVMTI 接口實現各種 debug 的功能,所以必須使用 C/C++ 語言編寫。而 debugger 端就沒有這樣的限制,可以使用任意語言編寫,只要遵守 JDWP 規范即可。JDI(Java Debug Interface)就包含了一個 Java 的 JDWP debugger 端的實現,JDK 中調試工具 jdb 也是使用 JDI 完成其調試功能的。
JDWP的更深介紹參考:?https://www.jianshu.com/p/134bd5b913c5
JDWP使用
服務器端
服務器端需要加載jdwp,設置方式如下:
CMD java -Djava.security.egd=file:/dev/./urandom $TRACK -jar $JAVA_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=9052 data-assets-biz.jar需要加載jdwp agent。?
參數說明:
- transport:指定了調試數據的傳送方式
- dt_socket:是指用SOCKET模式
- address: 端口或者HOST:PORT
- server=y/n VM 是否需要作為調試服務器執行。?
- suspend=y/n 在調試客戶端建立連接之后是否掛起。
本地端
IDEA 配置調試。
進行斷點調試時,遠程服務器會掛起。
Agent
jdwp使用的是agent技術。在JDK1.5以后,可以使用agent技術構建一個獨立于應用程序的代理程序(即為Agent),用來協助監測、運行甚至替換其他JVM上的程序。使用它可以實現虛擬機級別的AOP功能。
Agent分為兩種,一種是在主程序之前運行的Agent,一種是在主程序之后運行的Agent(前者的升級版,1.6以后提供)。
主程序
package study;public class AgentTest {public static void main(String[] args) {System.out.println("this is main process");} }在主程序運行之前的代理程序
1、編寫agent程序
package agent;import java.lang.instrument.Instrumentation;public class TestAgent {public static void premain(String agentArgs, Instrumentation ins){System.out.println("premain start...");System.out.println(agentArgs);} }2、配置MANIFEST.MF
<build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><version>3.2.0</version><configuration><archive><manifest><addClasspath>true</addClasspath></manifest><manifestEntries><Premain-Class>agent.TestAgent</Premain-Class></manifestEntries></archive></configuration></plugin></plugins></build>3、主程序加載agent程序
java -cp ./agent-test-0-1.0.jar -javaagent:./agent-test-pre-1.0.jar=x study.AgentTest輸出:
premain start...
x
this is main process?
agent詳解
參數 javaagent 可以用于指定一個 jar 包,并且對該 java 包有2個要求:
重點就在 premain 方法,也就是我們今天的標題。從字面上理解,就是運行在 main 函數之前的的類。當Java 虛擬機啟動時,在執行 main 函數之前,JVM 會先運行 -javaagent 所指定 jar 包內 Premain-Class 這個類的 premain 方法,其中,該方法可以簽名如下:
1.public static void premain(String agentArgs, Instrumentation inst) 2.public static void premain(String agentArgs)JVM 會優先加載 1 簽名的方法,加載成功忽略 2,如果1 沒有,加載 2 方法。
在主程序運行之后的代理程序
premain 只能在類加載之前修改字節碼,類加載之后無能為力,只能通過重新創建ClassLoader 這種方式重新加載。而 agentmain 就是為了彌補這種缺點而誕生的。簡而言之,agentmain 可以在類加載之后再次加載一個類,也就是重定義,你就可以通過在重定義的時候進行修改類了,甚至不需要創建新的類加載器,JVM 已經在內部對類進行了重定義。
但是這種方式對類的修改是由限制的,對比原來的老類,由如下要求:
1.父類是同一個;
2. 實現的接口數也要相同;
3. 類訪問符必須一致;
4. 字段數和字段名必須一致;
5. 新增的方法必須是 private static/final 的;
6. 可以刪除修改方法;
可以看的出來,相比較重新創建類加載器,限制還是挺多的,最重要的字段是無法修改的。因此,使用的時候要注意。
但是,agentmain 還有一個強大的特點,就是目標程序什么都不需要管,就能夠被代理。還記得 premain 是如何使用的嗎?需要在目標應用啟動的時候增加 -javaagent 參數。雖說沒有侵入性,但相比 agentmain 而言,還是有侵入性的,畢竟 agentmain 什么都不要。目標程序獨立運行,什么都不用管。
?
1、編寫agent程序
package agent;import java.lang.instrument.Instrumentation;public class TestAgent4 {public static void agentmain(String agentArgs , Instrumentation inst){System.out.println("load agent after main run.args=" + agentArgs);Class<?> [] classes = inst.getAllLoadedClasses();for(Class<?> c :classes){System.out.println(c.getCanonicalName());}System.out.println("agent run complete." );} }2、配置MANIFEST.MF
<build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><version>3.2.0</version><configuration><archive><manifest><addClasspath>true</addClasspath></manifest><manifestEntries><Agent-Class>agent.TestAgent4</Agent-Class></manifestEntries></archive></configuration></plugin></plugins></build>3、主程序加載agent程序
package study;public class AgentTest {public static void main(String[] args) {System.out.println("this is main process");List<VirtualMachineDescriptor> list = VirtualMachine.list();for (VirtualMachineDescriptor vmd : list) {if (vmd.displayName().endsWith("AccountMain")) {VirtualMachine virtualMachine = VirtualMachine.attach(vmd.id());virtualMachine.loadAgent("F:\\agent-test-post-1.0.jar");System.out.println("ok");virtualMachine.detach();}}} }需要先attach到一個VM,再加載agent。不是把agent加載到本VM。
?
輸出:
?
MANIFEST.MF參考:https://blog.csdn.net/demon7552003/article/details/106568285
?
?
?
?
?
總結
以上是生活随笔為你收集整理的Java Agent的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Versions maven plugi
- 下一篇: 怎样利用超图客户端打点_QuickFix