サーチ…


エージェントでクラスを変更する

まず、使用されているエージェントがManifest.mfに次の属性を持つことを確認します。

Can-Redefine-Classes: true
Can-Retransform-Classes: true

Javaエージェントを起動すると、エージェントはInstrumentationクラスにアクセスします。 Instrumentation では、addTransformer(ClassFileTransformerトランスフォーマ)を呼び出すことができます。 ClassFileTransformersを使用すると、クラスのバイトを書き換えることができます。クラスには、クラスをロードするClassLoader、クラスの名前、そのjava.lang.Classインスタンス、ProtectionDomain、最後にクラス自体のバイトを提供するメソッドが1つしかありません。

これは次のようになります。

byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, 
          ProtectionDomain protectionDomain, byte[] classfileBuffer)

純粋にバイトからクラスを変更するには年月がかかることがあります。これを解決するために、クラスバイトをより使いやすいものに変換するために使用できるライブラリがあります。

この例ではASMを使用しますが、JavassistやBCELのような他の選択肢にも同様の機能があります。

ClassNode getNode(byte[] bytes) {
    // Create a ClassReader that will parse the byte array into a ClassNode
    ClassReader cr = new ClassReader(bytes);
    ClassNode cn = new ClassNode();
    try {
        // This populates the ClassNode
        cr.accept(cn, ClassReader.EXPAND_FRAMES);
        cr = null;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return cn;
}

ここから、ClassNodeオブジェクトを変更することができます。これにより、フィールド/メソッドのアクセスを非常に簡単に変更できます。また、メソッドのバイトコードを変更するASMのTree APIにも簡単にアクセスできます。

編集が完了したら、次のメソッドでClassNodeをバイトに変換してから、 変換メソッドで返すことができます。

public static byte[] getNodeBytes(ClassNode cn, boolean useMaxs) {
    ClassWriter cw = new ClassWriter(useMaxs ? ClassWriter.COMPUTE_MAXS : ClassWriter.COMPUTE_FRAMES);
    cn.accept(cw);
    byte[] b = cw.toByteArray();
    return b;
}

実行時にエージェントを追加する

実行時にエージェントをJVMに追加することができます。エージェントを読み込むには、Attach APIのVirtualMachine.attatch(String id)を使用する必要があります。コンパイルされたエージェントjarをロードするには、次のメソッドを使用します。

public static void loadAgent(String agentPath) {
    String vmName = ManagementFactory.getRuntimeMXBean().getName();
    int index = vmName.indexOf('@');
    String pid = vmName.substring(0, index);
    try {
        File agentFile = new File(agentPath);
        VirtualMachine vm = VirtualMachine.attach(pid);
        vm.loadAgent(agentFile.getAbsolutePath(), "");
        VirtualMachine.attach(vm.id());
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

これは、ロードされたエージェントのpremain((String agentArgs、Instrumentation inst)を呼び出すのではなく、代わりにagentmain(String agentArgs、Instrumentation inst)を呼び出します。これはエージェントクラス Manifest.mfにエージェントクラスを設定する必要があります。

基本エージェントの設定

Premainクラスには、メソッド"premain(String agentArgs Instrumentation inst)"が含まれます

次に例を示します。

import java.lang.instrument.Instrumentation;

public class PremainExample {
    public static void premain(String agentArgs, Instrumentation inst) {
        System.out.println(agentArgs);
    }
}

jarファイルにコンパイルされたとき、Manifestを開き、Premain-Class属性を持つことを確認します。

次に例を示します。

Premain-Class: PremainExample

別のJavaプログラム "myProgram"でエージェントを使用するには、JVM引数にエージェントを定義する必要があります。

java -javaagent:PremainAgent.jar -jar myProgram.jar


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow