ホームページ >Java >&#&チュートリアル >Javaエージェントの使用方法

Javaエージェントの使用方法

PHPz
PHPz転載
2023-05-22 20:52:561875ブラウズ

Java エージェント テクノロジの概要

Java エージェントは、文字通り Java エージェントと訳され、Java プローブ テクノロジとも呼ばれます。

Java エージェント このテクノロジーは JDK1.5 で導入され、実行時に Java バイトコードを動的に変更できます。 Java のクラスは、JVM によって実行されるバイトコードを形成するためにコンパイルされます。JVM は、これらのバイトコードを実行する前にこれらのバイトコードの情報を取得し、バイトコード コンバータを通じてこれらのバイトコードを変更してプロセスを完了します。

Java エージェントは、独立して実行できない jar パッケージであり、ターゲット プログラムに接続された JVM プロセスを通じて動作します。起動時に、ターゲット プログラムの起動パラメータに -javaagent パラメータを追加して、ClassFileTransformer バイトコード コンバータを追加するだけです。これは、main メソッドの前にインターセプタを追加するのと同じです。

Java エージェントの機能紹介

Java エージェントには主に次の機能があります。

  • Java エージェント ロード前に Java バイトコードをインターセプトして変更できます;

  • #Java エージェントは、Jvm の実行中にロードされたバイトコードを変更できます;

  • ## Java エージェントのアプリケーション シナリオ:

Eclipse、IntelliJ IDEA などの IDE のデバッグ機能、
  • ホット デプロイメント機能などJRebel として、
  • # Visual VM、JConsole などのさまざまなパフォーマンス分析ツール、

  • # Skywalking、Pinpoint、などのフルリンク パフォーマンス検出ツールなど;
  • Java エージェントの実装原理
  • Java エージェント
  • の実装原理を理解する前に、Java エージェントについて明確に理解する必要があります。クラスロードメカニズム。 1 つは man メソッドを実行する前に premain で実行する方法、もう 1 つは実行中にプログラムを変更する方法で、JVM の Attach によって実装する必要があります。Attach の実装原理は JVMTI に基づいています。
  • 主にクラスをロードする前にバイトコードをインターセプトして変更します

これらの重要な用語をそれぞれ紹介しましょう:

#JVMTI

JVM ツール インターフェイス

で、ユーザー拡張のために JVM によって公開されるインターフェイスのコレクションです。JVMTI はイベント駆動型で、JVM が特定のロジックを実行するたびにトリガーされます。一部のイベントのコールバック インターフェイスこれらのコールバック インターフェイスを通じて、ユーザーは自分自身を拡張できます

JVMTI は、主流の Java 仮想マシンにデバッガー、プロファイラー、モニター、スレッド アナライザーなどのツールを実装するための統合基盤です。実装されている
  • #JVMTIAgent は、JVMTI によって公開されているいくつかのインターフェイスを使用して、実行したいが通常の状況では実行できないことを実行する動的ライブラリです。通常の動的ライブラリと区別するために、 通常、次のように 1 つ以上の関数を実装します。

    ##Agent_OnLoad
  • Function, if theエージェントは起動時にロードされます。JVM パラメータを通じて設定します。

    Agent_OnAttach

    関数。エージェントが起動時にロードされない場合は、最初にターゲット プロセスにアタッチします。その後、ロード コマンドを対応するターゲット プロセスに送信してロードします。Agent_OnAttach 関数
    • Agent_OnUnload 関数は、ロード プロセス中に呼び出されます。エージェントがアンインストールされるとき、これは

    • #javaagent

      と呼ばれ、機器の JVMTIAgent (Linux での対応する動的ライブラリは libinstrument.so) に依存します。また、個人的なものもあります。
    • JPLISAgent
    • (Java Programming Language Instrumentation Services Agent) という名前で、特に Java 言語で書かれたインストルメンテーション サービスのサポートを提供します

    • instrument
    は Agent_OnLoad を実装し、 Agent_OnAttach には 2 つのメソッドがあり、使用すると、エージェントを起動時にロードすることも、実行時に動的にロードすることもできます。起動時のロードでは、-javaagent:jar パッケージ パスと同様の方法で
  • instrument エージェント

    を間接的にロードすることもできます。実行時の動的ロードは JVM のアタッチ メカニズムに依存しており、エージェント はロードを送信することによってロードされます。

    JVM Attach
  • は、JVM が提供するプロセス間通信機能を指し、これにより、あるプロセスが別のプロセスにコマンドを渡し、スレッドなどの内部操作を実行できるようになります。 dump を実行するには、jstack を実行し、ダンプを実行する必要があるスレッドに pid とその他のパラメータを渡す必要があります。
  • Java Agent case使用します。 print メソッド 実行時間を例に挙げると、これは

    Java Agent
  • によって実現されます。
  • まず、合理化された Maven プロジェクトを構築する必要があります。このプロジェクトでは、プラグイン エージェントの実装用とテスト ターゲット プログラムの実装用の 2 つの Maven サブプロジェクトを構築します。

2 つのプロジェクトが共通の依存関係を持つパッケージを親アプリケーションにインポートします

    <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程序的入口

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エージェントの使用方法

最终效果:

Javaエージェントの使用方法

以上がJavaエージェントの使用方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。