java.lang.instrumentエージェントの使用
Java.lang.instrumentパッケージはJDK5で導入されました。プログラマーは、メソッドのバイトコードを変更することにより、クラスコードを動的に変更できます。これは通常、クラスの主な方法が呼び出される前に前処理され、Javaによって実装されてクラスのプロキシクラスを指定します。クラスのバイトコードがJVMにロードされる前に、ClassFileTransFormerの変換方法が呼び出され、元のクラスメソッドを変更してAOPを実装する機能を実現します。これの利点は、AOPを実装するDynamic ProxyやCGLIBテクノロジーなどの新しいクラスを生成せず、元のクラスがインターフェイスを持つ必要はないことです。
(1)エージェントは、メインメソッドの前にインターセプター、つまり、メインメソッドが実行される前にエージェントを実行するコードです。エージェントのコードは、メインメソッドと同じJVMで実行され、同じシステムクラスローダーによってロードされ、同じセキュリティポリシーとコンテキストによって管理されます。名前エージェントは少し誤解を招くものであり、私たちが一般的に理解しているエージェントとほとんど同じではありません。 Javaエージェントは比較的簡単に使用できます。 Javaエージェントを書く方法は? Premainメソッドのみを実装する必要があります:public static void premain(String AgentArgs、Instrumentation Inst)上記のPremainの定義がJDK 6で見つからない場合は、次のPremain定義を呼び出そうとします。
(2)エージェントクラスをJARパッケージに入力する必要があり、その後、Meta-inf/mainifest.mf内部には、Premainクラス属性が含まれている必要があります。 Manifest.mfの例は次のとおりです。
マニフェストバージョン:1.0 Premain-Class:myagent1 recrited-1.6.0_06
次に、JARパッケージにmanifest.mfを追加します。以下は、エージェントJARファイルのマニフェスト属性マニフェストです。PREMAIN-CLASS JVMが開始されたときにプロキシが指定されている場合、この属性はプロキシクラス、つまりPREMAINメソッドを含むクラスを指定します。 JVMの開始時にプロキシが指定されている場合、このプロパティが必要です。プロパティが存在しない場合、JVMは中止します。注:このプロパティは、ファイル名またはパスではなく、クラス名です。エージェントクラス実装が、VMの開始後の特定の瞬間にエージェントを起動するメカニズムをサポートする場合、このプロパティはエージェントクラスを指定します。つまり、AgentMainメソッドを含むクラス。このプロパティが必要であり、存在しない場合はプロキシは開始されません。注:これは、ファイル名またはパスではなく、クラス名です。ブートクラスパスは、ブートクラスローダー検索のパスリストを設定します。パスはディレクトリまたはライブラリを表します(通常、多くのプラットフォームでjarまたはzipライブラリと呼ばれます)。クラスを見つけるためのプラットフォーム固有のメカニズムが失敗した後、ブートクラスローダーはこれらのパスを検索します。リストされている順序でパスを検索します。リスト内のパスは、1つ以上のスペースで区切られています。パス階層URIのパスコンポーネント構文を使用します。パスがスラッシュ文字( "/")で始まる場合、それは絶対的なパスであり、それ以外の場合は相対的なパスです。相対パスは、プロキシJARファイルの絶対パスに基づいて解析されます。誤った形式と存在しないパスでパスを無視します。 VMが開始されてから特定の瞬間にエージェントが開始された場合、JARファイルを表すことのないパスは無視されます。このプロパティはオプションです。 can-redefineクラスブール(真または誤った、上限と小文字とは無関係)。このプロキシに必要なクラスを再定義できるかどうか。 true以外の値はfalseと見なされます。このプロパティはオプションであり、デフォルト値はfalseです。 can-retransform-classes boolean(真または誤った、上限と小文字とは無関係)。このプロキシに必要なクラスを再構成できるかどうか。 true以外の値はfalseと見なされます。このプロパティはオプションであり、デフォルト値はfalseです。 can-set-native-method-prefixブール値(真またはfalse、小文字と小文字とは無関係)。このプロキシに必要なネイティブメソッドプレフィックスを設定できるかどうか。 true以外の値はfalseと見なされます。このプロパティはオプションであり、デフォルト値はfalseです。
(3)これらすべてのエージェントJARパッケージは、プログラムのClassPathに自動的に追加されます。したがって、それらを手動でClassPathに追加する必要はありません。クラスパスの順序を指定したい場合を除きます。
(4)Javaプログラムの-Javaagentのパラメーターの数に制限はないため、Javaエージェントを追加することができます。すべてのJavaエージェントは、定義する順序で実行されます。例えば:
Java -javaagent:myagent1.jar -javaagent:myagent2.jar -jar myprogram.jar
myprogram.jarの主な関数がmyprogramにあるとします。 myagent1.jar、myagent2.jar、これら2つのJARパッケージでpremainを実装するクラスはmyagent1であり、myagent2プログラムの実行順序は次のとおりです。
myagent1.premain-> myagent2.premain-> myprogram.main
(5)さらに、メイン関数の後に配置されたPremainは実行されません。たとえば、:
Java -javaagent:myagent1.jar -jar myprogram.jar -javaagent:myagent2.jar
myagent2はmyprogram.jarの背後に配置されているため、myagent2の前提は実行されないため、実行結果は次のとおりです。
myagent1.premain-> myprogram.main
(6)各Javaエージェントは、String-Typeパラメーター、つまりPremainのAgentArgsを受信できます。このAgentArgsは、Javaオプションで定義されています。例えば:
Java -javaagent:myagent2.jar = thisisagentargs -jar myprogram.jar
Myagent2のPremainが受け取ったAgentargsの値は、「Thisisagentargs」(二重引用符を除く)です。
(7)パラメーターの計装:パラメーターで定義されたclassFileTransFormerを追加して、クラスファイルを変更します。ここでのカスタムトランスは、実際に実行されるクラスのバイトコードの変更を提供する変換法を実装し、別のクラスメソッドを実行するポイントに到達することさえできます。たとえば、エージェントクラスを書き込む:
Package org.toy; Import java.lang.instrument.instrumentation; import java.lang.instrument.classfiletransformer; public class perfmonagent {private static Instrumentation inst = null; /** *このメソッドは、アプリケーションのメインメソッドが呼び出される前に呼び出されます。 *このエージェントがJava VMに指定されたとき。 **/ public static void premain(string agentargs、instrumentation _inst){system.out.println( "perfmonagent.premain()が呼び出されました。"); //情報を追跡するために使用する静的変数を初期化します。 inst = _inst; //クラスファイルトランスをセットアップします。 classFileTransFormer trans = new PerfmonXFormer(); system.out.println( "jvmにperfmonxformerインスタンスを追加します。"); inst.addtransformer(trans); }}classfiletransformerクラスを書き込む:
Package org.toy; java.lang.instrument.classfiletransformer; Import java.lang.instrument.illegalclassformatexception; Import java.security.protectiondomain; Import javassist.cannotcompileeexception; import javassist.classist.ctbasist.ctgassist.ctpol javassist.notfoundexception; Import javassist.expr.expreditor; Import javassist.expr.methodcall; public class perfmonxformer classfiletransformer {] Transform(] Transform(] ClassLoader Loader、String className、classbeingedain、保護ドダイン保護ドダイン、[ {byte [] transformed = null; System.out.println( "Transforming" + className); classpool pool = classpool.getDefault(); ctclass cl = null; try {cl = pool.makeclass(new java.io.bytearrayinputStream(classFileBuffer)); if(cl.isinterface()== false){ctbehavior [] methods = cl.getdeclaredbehaviors(); for(int i = 0; i <methods.length; i ++){if(methods [i] .isempty()== false){domethod(methods [i]); }} transformed = cl.tobytecode(); }} catch(Exception e){System.err.println( "Can can instrument" + className + "、例外:" + e.getMessage()); }最後に{if(cl!= null){cl.detach(); }} return transformed; } private void domethod(ctbehavior method)throws notfoundexception、concancompileexception {// method.insertbefore( "long stime = system.nanotime();"); // method.insertafter( "system.out.println(/" leave "+method.getname()+" and time:/"+(system.nanotime() - stime));"); Method.instrument(new expreditor(){public void edit(methodcall m)throws can countcompileexception {m.replace( "{long stime = system.nanotime(); $ _ = $ colling($$); system.out.println(/" " +m.getclasname() +" +m.getmethodname() ":/"+(system.nanotime() - stime);} ");}}); }}); }}上記の2つのクラスがエージェントの中核です。 JVMが起動すると、Perfmonagent.Premainがアプリケーションがロードされる前に呼び出されます。次に、カスタマイズされたClassFileTransForme、つまりPerfmonXFormerがperfmonagent.premainでインスタンス化され、その後、カスタムClassFileTransFormerがPerfMonXFormerにインスタンス化され、PerfmonXFormerのインスタンスがインストルメントインスタンスに追加されます(JVMから送信)。これにより、アプリケーションのクラスがロードされたときにperfmonxformer.transformが呼び出されます。この方法でロードされたクラスを変更できます。それは本当に魔法です。クラスのバイトコードを変更するために、JbossのJavassistを使用しました。このように使用する必要はありませんが、JbossのJavassistは非常に強力で、クラスのバイトコードを簡単に変更できます。
上記の方法では、クラスのバイトコードを変更し、長いstime = system.nanotime()を追加しました。各クラスのメソッド入り、およびSystem.out.println( "methodclassname.methodname:"+(system.nanotime() - stime));
読んでくれてありがとう、私はそれがあなたを助けることができることを願っています。このサイトへのご支援ありがとうございます!