欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java Agent 動(dòng)態(tài)修改字節(jié)碼詳情

 更新時(shí)間:2021年09月08日 11:40:12   作者:老K的Java博客  
這篇文章主要介紹了Java Agent動(dòng)態(tài)修改字節(jié)碼的相關(guān)資料,需要的朋友可以參考下面文章具體的內(nèi)容

假設(shè)您有一個(gè)在生產(chǎn)環(huán)境中運(yùn)行的應(yīng)用程序。每隔一段時(shí)間,它就會(huì)進(jìn)入中斷狀態(tài),錯(cuò)誤很難重現(xiàn),您需要從應(yīng)用程序中獲得更多信息。

那么你想知道解決方案嗎?

您可以做的是動(dòng)態(tài)地將一些代碼集附加到應(yīng)用程序中,并仔細(xì)地重寫(xiě)它,以便代碼轉(zhuǎn)儲(chǔ)您可以記錄的其他信息,或者您可以將應(yīng)用程序階段轉(zhuǎn)儲(chǔ)到文本文件中。Java為我們提供了使用Java Agent實(shí)現(xiàn)這一點(diǎn)的工具。

你有沒(méi)有想過(guò)我們的Java代碼是如何在IDE中進(jìn)行熱交換的?這是因?yàn)樘毓?。關(guān)于Java Agent的另一個(gè)有趣的事實(shí)是,應(yīng)用程序探查器在后端使用相同的技術(shù)來(lái)收集內(nèi)存使用情況、內(nèi)存泄漏和方法執(zhí)行時(shí)間的信息。

1、什么是Java Agent

Java Agent是一種特殊類型的類,通過(guò)使用Java Instrumentation API,它可以攔截JVM上運(yùn)行的應(yīng)用程序,修改它們的字節(jié)碼。Java Agent非常強(qiáng)大,也非常危險(xiǎn)。

在開(kāi)始之前,我將解釋Java Agent如何使用簡(jiǎn)單的HelloWorld示例攔截類。

public class Hello {
    public static void main(String[] args){
        System.out.println("hello world");
    }
}


如下圖所示:

類加載器負(fù)責(zé)將類從二進(jìn)制加載到內(nèi)存中。運(yùn)行編譯后的HelloWorld應(yīng)用程序(HelloWorld.class)時(shí),可以將Agent視為在運(yùn)行時(shí)攔截類加載器行為的一種方式。您可能會(huì)想,java字節(jié)代碼是如何被重新構(gòu)造的,以便Agent可以在正確的位置添加相關(guān)代碼的。有趣的是,對(duì)于Java程序來(lái)說(shuō),字節(jié)碼的結(jié)構(gòu)非常接近原始Java程序源代碼。因此,雖然我們不為Java程序本身添加工具,但我們使用了它的一個(gè)非常接近的表示形式。需要注意的是,有一些非Java語(yǔ)言可以編譯成Java字節(jié)碼(如Scala、Clojure和Kotlin),這意味著程序字節(jié)碼的結(jié)構(gòu)和形狀可能會(huì)非常不同。

2、實(shí)現(xiàn)Java Agent

JavaAgent基于來(lái)自Java平臺(tái)的facility,它的入口點(diǎn)是Java.lang instrument包,它提供了允許Agent為JVM上運(yùn)行的程序提供工具的服務(wù)。該包非常簡(jiǎn)單且自包含,因?yàn)樗瑑蓚€(gè)異常類、一個(gè)數(shù)據(jù)類、類定義和兩個(gè)接口。在這兩種方法中,如果我們想編寫(xiě)Java Agent,我們只需要實(shí)現(xiàn)classFileTransformer接口。

定義代理有兩種方法。

第一個(gè)是靜態(tài)代理,這意味著我們構(gòu)建Agent代理,將其打包為jar文件,當(dāng)我們啟動(dòng)Java應(yīng)用程序時(shí),我們傳入一個(gè)名為java agent的特殊JVM參數(shù)。然后我們給它代理jar在磁盤(pán)上的位置,然后JVM發(fā)揮它的魔力。

$ java -javaagent:<path of agent jar file> -jar <path of the packaged jar file you want to intecept>


我們需要添加一個(gè)特殊的清單條目,稱為pre-main類,當(dāng)然,這是一個(gè)完全限定的名稱類定義。

Premain-Class : org.example.JavaAgent


這個(gè)類看起來(lái)像這樣

public class JavaAgent {
    /**
     * As soon as the JVM initializes, This  method will be called.
     *
     * @param agentArgs       The list of agent arguments
     * @param instrumentation The instrumentation object
     * @throws InstantiationException
     */
    public static void premain(String agentArgs, Instrumentation instrumentation) throws InstantiationException {
        InterceptingClassTransformer interceptingClassTransformer = new InterceptingClassTransformer();
        interceptingClassTransformer.init();
        instrumentation.addTransformer(interceptingClassTransformer);
    }
}


premain方法接受兩個(gè)參數(shù):

  • agentArgs—字符串參數(shù),用戶選擇作為參數(shù)傳遞給Java Agent調(diào)用的任何參數(shù)。
  • instrumentation來(lái)自java.lang instrument包,我們可以添加一個(gè)新的ClassFileTransformer對(duì)象,它包含Agent的實(shí)際邏輯。

第二個(gè)選項(xiàng)稱為動(dòng)態(tài)代理。

我們可以做的不是檢測(cè)啟動(dòng)應(yīng)用程序的方式,而是編寫(xiě)一小段代碼,接收并連接到現(xiàn)有JVM,并告訴它加載某個(gè)代理。

VirtualMachine vm = VirtualMachine.attach(vmPid);
vm.load(agentFilePath);
vm.detach();


此參數(shù)agentFilePath與靜態(tài)代理方法中的參數(shù)完全相同。它必須是agent jar的文件名,因此沒(méi)有輸入流也沒(méi)有字節(jié)。這種方法有兩個(gè)警告。第一個(gè)是,這是生活在com.sun空間下的私有API,它通常適用于熱點(diǎn)實(shí)現(xiàn)。第二個(gè)問(wèn)題是,使用Java9進(jìn)行排序時(shí),您不能再使用此代碼連接到它正在運(yùn)行的JVM。

2.1 類轉(zhuǎn)換

這是我們需要為agent實(shí)現(xiàn)的接口,以便轉(zhuǎn)換類。

public interface ClassFileTransformer {
    byte[] transform(ClassLoader loader, 
                     String className, 
                     Class<?> classBeingRedefined,
                     ProtectionDomain protectionDomain, 
                     byte[] classfileBuffer) 
            throws IllegalClassFormatException;
}

這有點(diǎn)多,但我將解釋方法簽名中的必要參數(shù)。第一個(gè)重要的是類名。此參數(shù)的主要目的是幫助查找并區(qū)分要攔截的正確類和其他類。顯然,您可能不想截取應(yīng)用程序中的每個(gè)類,最簡(jiǎn)單的方法是使用條件語(yǔ)句進(jìn)行檢查。

然后是類加載器,它主要用于基本應(yīng)用程序沒(méi)有平坦類空間的環(huán)境中,您可能不需要查看它,但一旦遇到更復(fù)雜或模塊化的平臺(tái),您就需要查看類加載器。classfileBuffer是檢測(cè)前類的當(dāng)前定義。要截取它,您需要使用庫(kù)讀取這個(gè)字節(jié)數(shù)組并截取代碼,然后必須再次轉(zhuǎn)換回字節(jié)碼才能返回。

有幾個(gè)字節(jié)碼生成庫(kù)。您需要進(jìn)行研究,并根據(jù)它是高級(jí)API還是低級(jí)API、社區(qū)大小和許可證自行決定。我放在下面的演示是Javassist,因?yàn)槲艺J(rèn)為它在高級(jí)和低級(jí)API之間有一個(gè)很好的平衡,并且是一個(gè)三重許可證,所以幾乎任何人都可以使用它。這

就是ClassFileTransformer實(shí)現(xiàn)的主體。

@Override
public byte[] transform(ClassLoader loader, ..)
        throws .. {
    byte[] byteCode = classfileBuffer;
    // If you wanted to intercept all the classs then you can remove this conditional check.
    if (className.equals("Example")) {
        try {
            ClassPool classPool = scopedClassPoolFactory.create(loader, rootPool,
                    ScopedClassPoolRepositoryImpl.getInstance());
            CtClass ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));
            CtMethod[] methods = ctClass.getDeclaredMethods();
            for (CtMethod method : methods) {
                if (method.equals("main")) {
                    method.insertAfter("System.out.println(\"Logging using Agent\");");
                }
            }
byteCode = ctClass.toBytecode();
            ctClass.detach();
        } catch (Throwable ex) {
            log.log(Level.SEVERE, "Error in transforming the class: " + className, ex);
        }
    }
    return byteCode;
}
ctClass.detach();
        } catch (Throwable ex) {
            log.log(Level.SEVERE, "Error in transforming the class: " + className, ex);
        }
    }
    return byteCode;
}

在這里,從類池中,我們可以直接繞過(guò)classfileBuffer獲取類,因?yàn)槲蚁胧褂?code>main方法。我們循環(huán)遍歷類定義中的所有方法,得到我們想要的類。我們根本不需要使用字節(jié)碼。我們可以簡(jiǎn)單地向它傳遞一些合法的Java代碼,然后Javassist將編譯它,生成新的字節(jié)碼,并給出定義。

有三種方法可以向方法中插入一些Java代碼。insertAfter(..)在正文末尾插入字節(jié)碼。它在正文的末尾插入字節(jié)碼。insertAt(..)在正文的指定行插入字節(jié)碼,insertBefore(..)在正文的開(kāi)頭插入字節(jié)碼。

2.2 使用Java代理進(jìn)行實(shí)際操作

從指定的鏈接下載示例應(yīng)用程序和Java Agent。

使用進(jìn)入路徑并執(zhí)行命令mvn clean install來(lái)構(gòu)建這兩個(gè)repo

現(xiàn)在,您將獲得目標(biāo)中的jar文件。復(fù)制示例應(yīng)用程序中.jar文件的路徑,并復(fù)制Java Agent中-dependencies.jar文件的路徑。

首先,使用命令$java-jar<path of the packaged jar>僅與示例應(yīng)用程序一起運(yùn)行應(yīng)用程序,并觀察輸出。Hi I am main。將在控制臺(tái)中打印。

然后,使用命令$ java -javaagent:<path of agent jar file> -jar <path of the packaged jar
file you want to intercept>
并觀察輸出。將在控制臺(tái)中另外打印使用代理的日志記錄。這確保java agent被攔截并添加到main方法的主體中。

總之,如果要實(shí)現(xiàn)Java Agent,請(qǐng)執(zhí)行以下操作:

1. 您需要?jiǎng)?chuàng)建兩個(gè)Java類。一個(gè)是premain方法(JavaAgent),另一個(gè)是擴(kuò)展ClassFileTransformer的類(CustomTransformer

2. 在premain方法的主體內(nèi),需要添加擴(kuò)展ClassFileTransformer的類的對(duì)象

3. 然后,您需要在CustomTransformer中的重寫(xiě)方法transform中添加邏輯。

4. 在轉(zhuǎn)換方法內(nèi)轉(zhuǎn)換字節(jié)碼時(shí),可能需要根據(jù)用途使用字節(jié)碼生成庫(kù)。

5. 您需要在清單中指定premain類并構(gòu)建jar。

6. 使用javaagent標(biāo)記將代理加載到要攔截的應(yīng)用程序中。

到此這篇關(guān)于Java Agent動(dòng)態(tài)修改字節(jié)碼詳情的文章就介紹到這了,更多相關(guān)Java Agent動(dòng)態(tài)修改字節(jié)碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 在Spring Boot中如何使用數(shù)據(jù)緩存

    在Spring Boot中如何使用數(shù)據(jù)緩存

    本篇文章主要介紹了在Spring Boot中如何使用數(shù)據(jù)緩存,具有一定的參考價(jià)值,有興趣的可以了解一下。
    2017-04-04
  • java查詢近七日數(shù)據(jù)功能的實(shí)現(xiàn)

    java查詢近七日數(shù)據(jù)功能的實(shí)現(xiàn)

    這篇文章主要介紹了java查詢近七日數(shù)據(jù)功能的實(shí)現(xiàn),文章內(nèi)容詳細(xì),簡(jiǎn)單易懂,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2023-01-01
  • IDEA報(bào)錯(cuò)Error?running‘Application‘:Command?line?is?too?long的問(wèn)題

    IDEA報(bào)錯(cuò)Error?running‘Application‘:Command?line?is?too?lo

    這篇文章主要介紹了IDEA報(bào)錯(cuò)Error?running?‘Application‘:Command?line?is?too?long的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • 一篇文章帶你入門(mén)Java多線程

    一篇文章帶你入門(mén)Java多線程

    這篇文章主要介紹了java多線程編程實(shí)例,分享了幾則多線程的實(shí)例代碼,具有一定參考價(jià)值,加深多線程編程的理解還是很有幫助的,需要的朋友可以參考下
    2021-08-08
  • Spring注解驅(qū)動(dòng)之ApplicationListener異步處理事件說(shuō)明

    Spring注解驅(qū)動(dòng)之ApplicationListener異步處理事件說(shuō)明

    這篇文章主要介紹了Spring注解驅(qū)動(dòng)之ApplicationListener異步處理事件說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • SpringBoot項(xiàng)目中如何訪問(wèn)HTML頁(yè)面

    SpringBoot項(xiàng)目中如何訪問(wèn)HTML頁(yè)面

    這篇文章主要介紹了SpringBoot項(xiàng)目中如何訪問(wèn)HTML頁(yè)面,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • java與php的區(qū)別淺析

    java與php的區(qū)別淺析

    在本篇文章里小編給大家整理了關(guān)于java與php的區(qū)別以及相關(guān)知識(shí)點(diǎn),有興趣的朋友們學(xué)習(xí)下。
    2019-03-03
  • java 如何將圖片按照原尺寸比例存入word中

    java 如何將圖片按照原尺寸比例存入word中

    這篇文章主要介紹了java 如何將圖片按照原尺寸比例存入word中的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Gateway集成Netty服務(wù)的配置加載詳解

    Gateway集成Netty服務(wù)的配置加載詳解

    這篇文章主要為大家介紹了Gateway集成Netty服務(wù)的配置加載詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • MyBatis執(zhí)行Sql的流程實(shí)例解析

    MyBatis執(zhí)行Sql的流程實(shí)例解析

    這篇文章主要介紹了MyBatis執(zhí)行Sql的流程實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12

最新評(píng)論