Java Instrumentation は非常に興味深く、それについてもっと知りたいと思っているので、Java Instrumentation を試しているところです。私はこれを javassist ライブラリと組み合わせて使用して、バイトコード操作をより簡単にし、JDK インストールに含まれる「ツール」ライブラリを使用しています。
これが私のメインクラスです:
public class MainClass {
public static boolean first = true;
static{
AgentClass.initialize();
}
public static void loadAgent(){
String path = System.getProperty("user.dir") + "\\AgentJar.jar";
String nameOfRunningVM = ManagementFactory.getRuntimeMXBean().getName();
int p = nameOfRunningVM.indexOf('@');
String pid = nameOfRunningVM.substring(0, p);
try {
VirtualMachine vm = VirtualMachine.attach(pid);
vm.loadAgent(path, "");
vm.detach();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public static void main(String[] args){
System.out.println("First run-through, code should be modified once.");
new Hello().hello();
first = false;
try {
AgentClass.getInstrumentation().retransformClasses(Class.forName("test.Hello"));
} catch (Exception e){
e.printStackTrace();
}
System.out.println("Second run-through, code should be modified twice.");
new Hello().hello();
}
}
「こんにちは」クラスは次のとおりです。
public class Hello {
public void hello(){
System.out.println("Hello World!");
}
}
FileTransformer クラスは次のとおりです。
public class FileTransformer implements ClassFileTransformer{
private static boolean first = true;
@Override
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
if (!className.contains("Hello"))
return null;
else{
byte[] result;
CtClass cc = null;
try {
cc = ClassPool.getDefault().get("test.Hello");
CtMethod method = cc.getDeclaredMethod("hello");
if (MainClass.first){
System.out.println("In transformer: first");
method.insertAfter("System.out.println(\"Modified First Time!\");");
}else{
System.out.println("In transformer: second");
method.insertAfter("System.out.println(\"I modified it again.!\");");
}
cc.writeFile();
result = cc.toBytecode();
} catch (Exception e) {
e.printStackTrace();
return null;
}
return result;
}
}
}
エージェント クラスは別の jar にあり、その基本的な実装です。
public class AgentClass {
protected static Instrumentation inst;
private static boolean added = false;
public static void agentmain(String args, Instrumentation inst){
AgentClass.inst = inst;
if (!added)
inst.addTransformer(new FileTransformer());
}
public static void premain(String args, Instrumentation inst){
AgentClass.inst = inst;
inst.addTransformer(new FileTransformer());
added = true;
}
public static void initialize(){
if (inst == null){
MainClass.loadAgent();
}
}
public static Instrumentation getInstrumentation(){
return inst;
}
}
実行すると、エラーは発生しません。ただし、出力は期待どおりではありません。
これが私が得る出力です:
First run-through, code should be modified once.
In transformer: first
Hello World!
Modified First Time!
Second run-through, code should be modified twice.
Hello World!
Modified First Time!
「I modified it again!」という行がないことに気付くかもしれません。
どんな助けでも大歓迎です。