Javaバイトコード2-instrument初体験
5101 ワード
文:オリジナル文章、転載は出典を明記してください。http://www.jianshu.com/p/be092b1c72cb
JavaバイトコードシリーズJavaバイトコード1-AgentシンプルJavaバイトコード2-instrumentはJavaバイトコード3を初めて体験します。ByteBddyを使ってJava-Agent Javaバイトコード4を実現します。Java-Agentを使ってJVM監視ツールの本シリーズのコードを実現します。https://github.com/hawkingfoo/demo-agent
一、概要
前のセクションではJavaバイトコード1−Agentを簡単に手に入れて、Agentによって
二、一つの関数を実現して、消耗時のAgentを検出します。
1、pom.xmlを修正する
ここでは前のセクションとは異なり、Agentを強化するために
3、Transformerクラスを実現する
三、運転
前のセクションの動作と同じように、このセクションの内容を反映するために、Testクラスを修正し、二つの方法を追加しました。
JavaバイトコードシリーズJavaバイトコード1-AgentシンプルJavaバイトコード2-instrumentはJavaバイトコード3を初めて体験します。ByteBddyを使ってJava-Agent Javaバイトコード4を実現します。Java-Agentを使ってJVM監視ツールの本シリーズのコードを実現します。https://github.com/hawkingfoo/demo-agent
一、概要
前のセクションではJavaバイトコード1−Agentを簡単に手に入れて、Agentによって
main
方法の前で実行できることを理解した。本セクションでは、java.lang.instrument
によって、クラスのバイトコードが1つのAgentを実現することができるinstrument
を紹介します。次に、javassist
を介して簡単な性能検出ツールを実現します。目的は、検出関数の呼び出し時に、ここではレンガを投げて玉を引くだけで、instrument
が提供するより緩やかな結合のAOPはこれだけではない。二、一つの関数を実現して、消耗時のAgentを検出します。
1、pom.xmlを修正する
ここでは前のセクションとは異なり、Agentを強化するために
javassist
パッケージを追加的に導入する必要がある。javassistjavassist
3.12.1.GA
jar
この他に、このJarパッケージをAgentに包装したいです。以下の構成を参照してください。
org.apache.maven.plugins
maven-shade-plugin
package
shade
javassist:javassist:jar:
2、Agentを一つ実現するpublic class MyAgent {
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("this is an perform monitor agent.");
// Transformer
ClassFileTransformer transformer = new PerformMonitorTransformer();
inst.addTransformer(transformer);
}
}
前のセクションとは異なり、ここにTransformer
が追加されている。3、Transformerクラスを実現する
public class PerformMonitorTransformer implements ClassFileTransformer {
private static final Set classNameSet = new HashSet<>();
static {
classNameSet.add("com.example.demo.AgentTest");
}
@Override
public byte[] transform(ClassLoader loader,
String className,
Class> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
try {
String currentClassName = className.replaceAll("/", ".");
if (!classNameSet.contains(currentClassName)) { // Set
return null;
}
System.out.println("transform: [" + currentClassName + "]");
CtClass ctClass = ClassPool.getDefault().get(currentClassName);
CtBehavior[] methods = ctClass.getDeclaredBehaviors();
for (CtBehavior method : methods) {
enhanceMethod(method);
}
return ctClass.toBytecode();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private void enhanceMethod(CtBehavior method) throws Exception {
if (method.isEmpty()) {
return;
}
String methodName = method.getName();
if (methodName.equalsIgnoreCase("main")) { // main
return;
}
final StringBuilder source = new StringBuilder();
source.append("{")
.append("long start = System.nanoTime();
") // :
.append("$_ = $proceed($$);
") //
.append("System.out.print(\"method:[" + methodName + "]\");").append("
")
.append("System.out.println(\" cost:[\" +(System.nanoTime() -start)+ \"ns]\");") //
.append("}");
ExprEditor editor = new ExprEditor() {
@Override
public void edit(MethodCall methodCall) throws CannotCompileException {
methodCall.replace(source.toString());
}
};
method.instrument(editor);
}
}
上のコードの中で、Setに保存されているクラスの方法を強化しました。ここでは「comp.example.demo.AgentTest」です。他のクラスであれば、ここをファイルに再設定または配置する必要があります。enhanceMethod
方法により、その方法のバイトコードが書き換えられ、すなわち元の方法体に対してサラウンド増強が行われた。三、運転
前のセクションの動作と同じように、このセクションの内容を反映するために、Testクラスを修正し、二つの方法を追加しました。
public class AgentTest {
private void fun1() {
System.out.println("this is fun 1.");
}
private void fun2() {
System.out.println("this is fun 2.");
}
public static void main(String[] args) {
AgentTest test = new AgentTest();
test.fun1();
test.fun2();
}
運転結果は以下の通りです。this is an perform monitor agent.
transform: [com.example.demo.AgentTest]
this is fun 1.
method:[fun1] cost:[79024ns]
this is fun 2.
method:[fun2] cost:[41164ns]
Process finished with exit code 0
上記の運転結果から、呼び出しの2つの方法を強化し、呼び出しの時間がかかります。