中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java?Agent怎么用

發布時間:2022-04-27 16:23:15 來源:億速云 閱讀:213 作者:iii 欄目:開發技術

這篇文章主要介紹“Java Agent怎么用”,在日常操作中,相信很多人在Java Agent怎么用問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Java Agent怎么用”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

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 運行期間修改已經加載的字節碼;

Java Agent 的應用場景:

  • IDE 的調試功能,例如 Eclipse、IntelliJ IDEA ;

  • 熱部署功能,例如 JRebel、XRebel、spring-loaded;

  • 各種線上診斷工具,例如 Btrace、Greys,還有阿里的 Arthas;

  • 各種性能分析工具,例如 Visual VM、JConsole 等;

  • 全鏈路性能檢測工具,例如 Skywalking、Pinpoint等;

Java Agent 實現原理

在了解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項目,在其中構建兩個Maven的子項目,一個用于實現外掛的Agent,一個用于實現測試目標程序。

Java?Agent怎么用

我們在父應用中導入兩個項目公共依賴的包

    <dependencies>
        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.28.0-GA</version>
        </dependency>
    </dependencies>

首先我們去構建測試的目標程序

// 啟動類
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();
        }
    }
}

然后我們啟動程序,測試是否能正常執行,程序正常執行之后,我們開始構建探針程序

探針程序中我們需要編寫,改變原有class的Transformer,通過自定義的Transformer類完成輸出方法執行時間的功能,

Java?Agent怎么用

首先構檢Agent程序的入口

public class RunTimeAgent {
    public static void premain(String arg, Instrumentation instrumentation) {
        System.out.println("探針啟動!!!");
        System.out.println("探針傳入參數:" + arg);
        instrumentation.addTransformer(new RunTimeTransformer());
    }
}

這里每個類加載的時候都會走這個方法,我們可以通過className進行指定類的攔截,然后借助javassist這個工具,進行對Class的處理,這里的思想和反射類似,但是要比反射功能更加強大,可以動態修改字節碼。

javassist是一個開源的分析、編輯和創建Java字節碼的類庫。

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;
    }
}

我們需要在Maven中配置,編譯打包的插件,這樣我們就可以很輕松的借助Maven生成Agent的jar包

<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>

否則我們需要在resources下創建META-INF/MANIFEST.MF文件,文件內容如下,我們可以看出這個與Maven中的配置是一致的,然后通過配置編譯器,借助編譯器打包成jar包,需指定該文件

Manifest-Version: 1.0
Premain-Class: com.zhj.agent.RunTimeAgent
Can-Redefine-Classes: true
Can-Retransform-Classes: true

告示文件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參數即可

參數示例:-javaagent:F:\code\myCode\agent-test\runtime-agent\target\runtime-agent-1.0-SNAPSHOT.jar=hello

Java?Agent怎么用

最終效果:

Java?Agent怎么用

到此,關于“Java Agent怎么用”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

东阿县| 敦煌市| 体育| 营山县| 中江县| 桑植县| 莱芜市| 平阳县| 永年县| 陇南市| 同江市| 苍梧县| 寿阳县| 潢川县| 辽中县| 虎林市| 象山县| 旌德县| 炉霍县| 丰原市| 元氏县| 闽侯县| 勃利县| 芜湖县| 基隆市| 家居| 合作市| 同仁县| 平利县| 文山县| 大埔县| 秭归县| 黄大仙区| 沁阳市| 罗城| 桂东县| 新绛县| 英德市| 承德县| 濮阳市| 南皮县|