Java Agent 技術簡介
Java Agent 直譯為 Java 代理,也常被稱為 Java 探針技術。
Java Agent 這種技術是在 JDK1.5 引入的,可以在執行時間動態修改 Java 字節碼。 Java 中的類別編譯後形成字節碼被JVM 執行,在JVM 在執行這些字節碼之前獲取這些字節碼的信息,並且透過字節碼轉換器對這些字節碼進行修改,以此來完成一些額外的功能。
Java Agent 是一個無法獨立執行 jar 套件,它透過依附於目標程式的 JVM 進程,進行工作。啟動時只需要在目標程式的啟動參數中加入-javaagent 參數新增 ClassFileTransformer
字節碼轉換器,相當於在main方法前加了一個攔截器。
Java Agent 功能介紹
Java Agent 主要有下列功能:
##Java Agent
能夠在載入Java 字節碼之前攔截並對字節碼進行修改;
- Java Agent 能夠在Jvm 運行期間修改已經載入的字節碼;
- #IDE 的偵錯功能,例如Eclipse、IntelliJ IDEA ;
- #熱部署功能,例如JRebel、XRebel、spring-loaded;
- #各種線上診斷工具,例如Btrace、Greys,還有阿里的Arthas;
- 各種效能分析工具,例如Visual VM、JConsole 等;
- #全連結效能偵測工具,例如Skywalking、Pinpoint等;
在了解
Java Agent的實作原理之前,需要對Java類別載入機制有一個較為清晰的認知。一種是在man方法執行之前,透過premain來執行,另一種是程式運行中修改,需透過JVM中的Attach實現,Attach的實作原理是基於JVMTI。 主要是在類別載入之前,進行攔截,對字節碼修改
- #JVMTI
就是JVM Tool Interface,是JVM 揭露出來給使用者擴充功能使用的介面集合,JVMTI 是基於事件驅動的,JVM每執行一定的邏輯就會觸發一些事件的回調接口,透過這些回呼接口,使用者可以自行擴展
JVMTI是實現Debugger、Profiler、Monitor、Thread Analyser 等工具的統一基礎,在主流Java 虛擬機中都有實作
- JVMTIAgent
是一個動態函式庫,利用JVMTI暴露出來的一些介面來幹一些我們想做、但是正常情況下又做不到的事情,不過為了和普通的動態庫進行區分,它一般會實現如下的一個或多個函數:
- Agent_OnLoad
函數,如果agent是在啟動時載入的,透過JVM參數設定
- Agent_OnAttach
函數,如果agent不是在啟動時載入的,而是我們先attach到目標進程上,然後給對應的目標進程發送load命令來加載,則在加載過程中會調用Agent_OnAttach函數
- Agent_OnUnload
函數,在agent卸載時呼叫
- javaagent
依賴instrument的JVMTIAgent(Linux下對應的動態函式庫是libinstrument.so),還有個別名叫JPLISAgent(Java Programming Language Instrumentation Services Agent),專門為Java語言編寫的插樁服務提供支援的
- instrument
實作了Agent_OnLoad和Agent_OnAttach兩方法,也就是說在使用時,agent既可以在啟動時加載,也可以在運行時動態加載。其中啟動時載入也可以透過類似-javaagent:jar包路徑的方式來間接載入instrument agent,運行時動態載入依賴的是JVM的attach機制,透過發送load指令來載入agent
- JVM Attach
是指JVM 提供的一種進程間通訊的功能,能讓一個進程傳命令給另一個進程,並進行一些內部的操作,例如進行線程dump,那麼就需要執行jstack 進行,然後把pid 等參數傳遞給需要dump 的線程來執行
Java Agent 案例
我們就以打印方法的執行時間為例,透過
Java Agent來實作。 首先我們需要建立一個精簡的
項目,在其中建立兩個Maven的子項目,一個用於實作外掛的Agent,一個用於實作測試目標程式。
<dependencies>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.28.0-GA</version>
</dependency>
</dependencies>
然后我们启动程序,测试是否能正常执行,程序正常执行之后,我们开始构建探针程序 探针程序中我们需要编写,改变原有class的 首先构检Agent程序的入口 这里每个类加载的时候都会走这个方法,我们可以通过className进行指定类的拦截,然后借助javassist这个工具,进行对Class的处理,这里的思想和反射类似,但是要比反射功能更加强大,可以动态修改字节码。 javassist是一个开源的分析、编辑和创建Java字节码的类库。 我们需要在Maven中配置,编译打包的插件,这样我们就可以很轻松的借助Maven生成Agent的jar包 否则我们需要在resources下创建META-INF/MANIFEST.MF文件,文件内容如下,我们可以看出这个与Maven中的配置是一致的,然后通过配置编译器,借助编译器打包成jar包,需指定该文件 告示文件MANIFEST.MF参数说明: Manifest-Version 文件版本 Premain-Class 包含 premain 方法的类(类的全路径名)main方法运行前代理 Agent-Class 包含 agentmain 方法的类(类的全路径名)main开始后可以修改类结构 Boot-Class-Path 设置引导类加载器搜索的路径列表。查找类的特定于平台的机制失败后,引导类加载器会搜索这些路径。按列出的顺序搜索路径。列表中的路径由一个或多个空格分开。(可选) Can-Redefine-Classes true 表示能重定义此代理所需的类,默认值为 false(可选) Can-Retransform-Classes true 表示能重转换此代理所需的类,默认值为 false (可选) Can-Set-Native-Method-Prefix true 表示能设置此代理所需的本机方法前缀,默认值为 false(可选) 最后通过Maven生成Agent的jar包,然后修改测试目标程序的启动器,添加JVM参数即可 参数示例: 最终效果:// 启动类
public class APPMain {
public static void main(String[] args) {
System.out.println("APP 启动!!!");
AppInit.init();
}
}
// 模拟的应用初始化的类
public class AppInit {
public static void init() {
try {
System.out.println("APP初始化中...");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Transformer
,通过自定义的Transformer类完成输出方法执行时间的功能,public class RunTimeAgent {
public static void premain(String arg, Instrumentation instrumentation) {
System.out.println("探针启动!!!");
System.out.println("探针传入参数:" + arg);
instrumentation.addTransformer(new RunTimeTransformer());
}
}
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
public class RunTimeTransformer implements ClassFileTransformer {
private static final String INJECTED_CLASS = "com.zhj.test.init.AppInit";
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
String realClassName = className.replace("/", ".");
if (realClassName.equals(INJECTED_CLASS)) {
System.out.println("拦截到的类名:" + realClassName);
CtClass ctClass;
try {
// 使用javassist,获取字节码类
ClassPool classPool = ClassPool.getDefault();
ctClass = classPool.get(realClassName);
// 得到该类所有的方法实例,也可选择方法,进行增强
CtMethod[] declaredMethods = ctClass.getDeclaredMethods();
for (CtMethod method : declaredMethods) {
System.out.println(method.getName() + "方法被拦截");
method.addLocalVariable("time", CtClass.longType);
method.insertBefore("System.out.println(\"---开始执行---\");");
method.insertBefore("time = System.currentTimeMillis();");
method.insertAfter("System.out.println(\"---结束执行---\");");
method.insertAfter("System.out.println(\"运行耗时: \" + (System.currentTimeMillis() - time));");
}
return ctClass.toBytecode();
} catch (Throwable e) { //这里要用Throwable,不要用Exception
System.out.println(e.getMessage());
e.printStackTrace();
}
}
return classfileBuffer;
}
}
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<!-- 指定maven编译的jdk版本。若不指定,maven3默认用jdk 1.5 maven2默认用jdk1.3 -->
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<!--自动添加META-INF/MANIFEST.MF -->
<manifest>
<addClasspath>true</addClasspath>
</manifest>
<manifestEntries>
<Menifest-Version>1.0</Menifest-Version>
<Premain-Class>com.zhj.agent.RunTimeAgent</Premain-Class>
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<Can-Retransform-Classes>true</Can-Retransform-Classes>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
Manifest-Version: 1.0
Premain-Class: com.zhj.agent.RunTimeAgent
Can-Redefine-Classes: true
Can-Retransform-Classes: true
-javaagent:F:\code\myCode\agent-test\runtime-agent\target\runtime-agent-1.0-SNAPSHOT.jar=hello
以上是Java Agent怎麼用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本文討論了使用Maven和Gradle進行Java項目管理,構建自動化和依賴性解決方案,以比較其方法和優化策略。

本文使用Maven和Gradle之類的工具討論了具有適當的版本控制和依賴關係管理的自定義Java庫(JAR文件)的創建和使用。

本文討論了使用咖啡因和Guava緩存在Java中實施多層緩存以提高應用程序性能。它涵蓋設置,集成和績效優勢,以及配置和驅逐政策管理最佳PRA

本文討論了使用JPA進行對象相關映射,並具有高級功能,例如緩存和懶惰加載。它涵蓋了設置,實體映射和優化性能的最佳實踐,同時突出潛在的陷阱。[159個字符]

Java的類上載涉及使用帶有引導,擴展程序和應用程序類負載器的分層系統加載,鏈接和初始化類。父代授權模型確保首先加載核心類別,從而影響自定義類LOA


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

SublimeText3漢化版
中文版,非常好用

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),