シンプルJavaAgentの実現

まず、javaagentは何ですか

javaagentは、「プラグイン」JVM、特別に細工された.jarファイルである、それは利点計装API JVMを提供して取ることができます。

1.1、概要

Javaのエージェントは、3つの部分から構成:プロキシクラス、メタ情報およびプロキシクラスローディング機構との.jar JVM剤、コンテンツ全体を次のように:
ここに画像を挿入説明

1.2、礎石のjavaagent

java.lang.instrumentJVMサービス上で実行されているjavaagent方式のバイトコードプログラムの方法を変更することによって。JARパッケージとして展開javaagentは、エージェントを起動するためのプロキシクラスのJARマニフェストファイルをロードする属性を指定します。

次の方法を開始しjavaagent:

  • コマンドラインでパラメータを指定して起動します。

  • スタートJVMが開始しました。例えば、既に実行中のアプリケーションに取り付けることができるツールを提供し、アプリケーションにロードエージェントが実行されている可能にします。

  • アプリケーションで、実行可能ファイルとしてパッケージ化。

1.3、スタートjavaagent

1.3.1、開始するには、コマンドライン

次のパラメータを開始するには、コマンドライン:

-javaagent:<jarpath>[=<options>]

<jarpath>:Javaagentパス、例えば/opt/var/Agent-1.0.0.jar
<options>:Javaagentパラメータ、分析パラメータ責任javaagent。
javaagent JARマニフェストファイルが含まれている必要がありますPremain-Class属性を、エージェントクラス属性値のフルパス名(パッケージ名+クラス名)。Agentクラスが実装しなければならないpremainメソッド、premainメソッド、およびmainメソッドは、エントリポイントとアプリケーションプロキシと同じです。最初は、JVMの初期化が完了した後、エージェントpremain関数を呼び出すと、premainメソッドは、起動するプロセスを返さなければなりませんした後、その後、アプリケーションのメイン関数を呼び出します。

premain メソッドシグネチャを次のように

public static void premain(String agentArgs, Instrumentation inst)
public static void premain(String agentArgs)

エージェントクラスは、メソッドシグネチャを実装していない場合、プロキシにメソッドシグネチャを呼び出すJVM最初の試みは、方法2を呼び出す署名JVMの試み:

演技のクラスが持つことができるagentmain機能を、関数呼び出しは、JVMの完了後に開始されます。あなたは、エージェントを開始するには、コマンドラインを使用している場合、agentmain方法は呼び出されません。

すべてのパラメータは、文字列でエージェントとして扱われるagentArgsパラメータ文字列を解析するためのエージェントが担当し、可変変速機。

エージェントクラスをロードできないため、エージェントは、実装していないエージェントクラス場合はpremainメソッドが例外またはキャッチされないをスローし、JVMは終了します。

javaagentスタートの実装は、コマンドラインからの達成支援を開始した場合、実装は指定して、コマンドラインをサポートしている必要があり、特定のコマンドラインを提供するために必要とされていない-javaagent起動パラメータを。-javaagent複数のエージェントを起動し、コマンドラインで複数回使用することができます。premainコマンドラインで指定された順序で配列し、関数を呼び出すことは一貫して、薬剤の同じ複数を使用することができます<jarpath>

定義しない厳格なモデルpremainの仕事関数の範囲を、任意のmain機能は、このようなスレッドの作成など、仕事をすることができ、premain機能は有効です。

1.3.2、JVM起動時のブート

実装は、JVMの起動後にエージェントを再起動するための機構を提供することができます。エージェント実装固有の詳細を開始するにはどのように、通常はアプリケーションが起動され、そのmainメソッドが呼び出されています。実装がサポートするブート・エージェントJVMが起動している場合、エージェントは次の基準を満たす必要があります。

  • マニフェストファイルが含まれているAgent-Class属性は、プロキシクラスのフルネームで、属性を。

  • Agentクラスが実装しなければならないpublic static agentmain方法を。

agentmain署名方法は、以下の2つの機能を有します。

public static void agentmain(String agentArgs, Instrumentation inst)
public static void agentmain(String agentArgs)

メソッドシグネチャを呼び出すJVM最初の試みエージェントクラスは、このメソッドを実装していない場合、メソッドシグネチャ2を起動するJVMの試み1を有しています。

演技のクラスが同時に達成することができますpremainし、agentmainエージェントがJVM呼び出し、コマンドラインを起動したときに、二つの方法premainスタートJVM後に開始したエージェントは、JVMが呼び出したときに、機能をagentmain機能を、そしてJVMが呼び出されないpremain機能を。

agentmainまた、伝達関数パラメータによってagentArgs、すべてのパラメータは1つの文字列、解像度パラメータを担当するエージェントに統合されています。

agentmain機能は、起動が完了したときに、必要なすべてのブートエージェントの初期化操作を完了しなければならないagentmain関数が返す必要があります。エージェントが起動またはキャッチされない例外をスローすることができない場合は、JVMは終了します。

実行可能ファイルとしてパッケージ1.3.3、

エージェントが実行可能JARファイルにパッケージ化されている場合は、実行可能なJARファイルのリストが含まれている必要がありますLauncher-Agent-Class主な機能の開始がクラスを呼び出す前に、アプリケーションでプロキシを指定する属性を。JVMプロキシのメソッドを呼び出そう。

public static void agentmain(String agentArgs, Instrumentation inst)

エージェントクラスは、上記の方法を実装していない場合は、JVM以下のメソッドが呼び出されます。

public static void agentmain(String agentArgs)

agentArgs パラメータは、空の文字列でなければなりません。

agentmain初期化機能は、エージェントが起動し、起動後に返却しなければならないすべてのアクションを完了する必要があります。エージェントが起動に失敗したり、キャッチされない例外をスローした場合、JVMは終了します。

1.3.4、ローディングモジュールエージェントクラスと利用可能なプロキシクラス/クラス

システムクラスローダは、すべてのクラスのエージェントJARファイルをロードするための責任がある、と無名のモジュールシステムクラスローダの一員となりました。このシステムはまた、一般的に定義されたクラスローダアプリケーションが含まれているmainクラスメソッドを。プロキシクラスのすべてのクラスが見えるシステムクラスローダに表示され、次の最小要件を満たしている必要があります。

  • スタート層モジュールは、パッケージ内のクラスを派生しました。層を起動すると、プラットフォームモジュールまたはアプリケーションモジュールに応じて、すべての最初の起動モードが含まれています。

  • クラスは、システムクラスローダーとして定義することができます。

  • すべてのプロキシクラスは、クラスローダモジュールは、そのメンバーは、名前の定義され始めます。

プロキシクラス層のプラットフォーム(または他の)起動しないモジュールクラスにリンクする必要がある場合は、これらのモジュールは、アプリケーションを起動する方法を開始する層に配置されていることを確認する必要があります。例えば、JDKの実装では、--add-modulesコマンドラインオプションは、開始濃縮ルートモジュールで解決されるモジュールを追加するために使用することができます。

スタートクラスローダは、プロキシサポートクラス(で読み込むことができるappendToBootstrapClassLoaderSearch唯一のカスタムブートクラスローダクラスにリンクしなければならないか、指定されたブート-Class-Path属性を)。私たちは、ブートクラスローダは、すべてのプラットフォーム上で動作することを保証することはできません。

(によって構成されるカスタム・システム・クラス・ローダ場合はgetSystemClassLoader指定されたメソッドのシステムプロパティjava.system.class.loader)、定義されなければならないappendToSystemClassLoaderSearch指定にappendToClassPathForInstrumentation方法。つまり、カスタムクラスローダシステムは、検索範囲内のシステムクラスローダのメカニズムへのエージェントのJARファイルの追加をサポートしている必要があります。

1.4、javaagent一覧のプロパティ

プロパティ 説明 必要とされます デフォルト値
Premainクラス Premainクラスメソッド備えます スタート依存的 ノー
エージェントクラス クラスのagentmainを含む方法 スタート依存的 ノー
ブートクラスパス スタートクラスローダの検索パス ノー ノー
CAN-再定義-Classis 私は必要なプロキシクラスを再定義することができます ノー
CAN-再変換-Classis あなたは、このエージェントが必要とするクラスを再変換することができます ノー
CAN-セットネイティブメソッドのプレフィックス このエージェントは、必要に応じて、ネイティブメソッドの接頭辞を提供することができます ノー

第二に、Javaのエージェントを書きます

上記の説明に基づいて、我々は、すべての非システムクラスがダウンロードjavaagent JVMを達成します。

開発プロセス全体を通じて、以下の3つのステップを含みます。

  • 1)プロキシクラスの定義、実装クラスのダウンロード。

  • 2)配置パッケージ。

  • 3)コマンドラインはテストを開始します。

2.1、プロキシクラスが実装

実装premainの機能を

package io.ct.java.agent;

import java.lang.instrument.Instrumentation;

public class AgentApplication {
    public static void premain(String arg, Instrumentation instrumentation) {
        System.err.println("agent startup , args is " + arg);
        // 注册我们的文件下载函数
        instrumentation.addTransformer(new DumpClassesService());
    }
}

ダウンロードクラスが実装ClassFileTransformerクラスは、クラスのバイトコードロードダウンロードされるインタフェース:

package io.ct.java.agent;

import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import java.util.Arrays;
import java.util.List;

/**
 * Copyright (C), 2018-2018, open source
 * FileName: DumpClassesService
 *
 * @author : 大哥
 * Date:     2018/12/8 21:01
 */
public class DumpClassesService implements ClassFileTransformer {
    private static final List<String> SYSTEM_CLASS_PREFIX = Arrays.asList("java", "sum", "jdk");

    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
        if (!isSystemClass(className)) {
            System.out.println("load class " + className);
            FileOutputStream fos = null;
            try {
                // 将类名统一命名为classNamedump.class格式
                fos = new FileOutputStream(className + "dump.class");
                fos.write(classfileBuffer);
                fos.flush();
            } catch (IOException ioe) {
                ioe.printStackTrace();
            } finally {
                // 关闭文件输出流
                if (null != fos) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return classfileBuffer;
    }

    /**
     * 判断一个类是否为系统类
     *
     * @param className 类名
     * @return System Class then return true,else return false
     */
    private boolean isSystemClass(String className) {
        // 假设系统类的类名不为NULL而且不为空
        if (null == className || className.isEmpty()) {
            return false;
        }

        for (String prefix : SYSTEM_CLASS_PREFIX) {
            if (className.startsWith(prefix)) {
                return true;
            }
        }
        return false;
    }
}

2.2、コンフィギュレーションMANIFEST.MF

MANIFEST.MF二つの方法で生成されたファイル:だけする必要があり、手動と自動構成生成、手動設定resources次のファイルを作成するMETA-INF/MENIFEST.MFファイルを。手動外形を除去して自動的に次のような構成のプラグインのmaven、ステージパッキングMavenのプラグを使用して生成することができます。

             <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <Premain-Class>io.ct.java.agent.AgentApplication</Premain-Class>
                            <Agent-Class>io.ct.java.agent.AgentApplication</Agent-Class>
                            <Can-Redefine-Classes>true</Can-Redefine-Classes>
                            <Can-Retransform-Classes>true</Can-Retransform-Classes>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>

ジャー次の形式の生成:
ここに画像を挿入説明
MANIFEST.MFファイルは、以下の特徴(異なる構成のコンテンツファイルを生成することは正確ではありません)。

Manifest-Version: 1.0
Implementation-Title: agent
Premain-Class: io.ct.java.agent.AgentApplication
Implementation-Version: 0.0.1-SNAPSHOT
Built-By: chentong
Agent-Class: io.ct.java.agent.AgentApplication
Can-Redefine-Classes: true
Implementation-Vendor-Id: io.ct.java
Can-Retransform-Classes: true
Created-By: Apache Maven 3.5.4
Build-Jdk: 1.8.0_171
Implementation-URL: https://projects.spring.io/spring-boot/#/spring-bo
 ot-starter-parent/agent

2.3、Javaのエージェントを開始するには、コマンドライン

こんにちは、すでにコンパイルされたクラスを実行するには、次のコマンドを実行し、同じディレクトリにHellodump.classという名前のファイルを生成することができます。

java -javaagent:agent-0.0.1-SNAPSHOT.jar Hello
公開された100元の記事 ウォン称賛45 ビュー640 000 +

おすすめ

転載: blog.csdn.net/wangzhongshun/article/details/100287986