java agent技术(二)
premain
premain发方法是在JVM启动的时候运行的,但是真正产生修改字节码的动作是在类被加载的时候发生的
public static void premain(String args, Instrumentation inst) {inst.addTransformer(new ClassFileTransformer() {@Overridepublic byte[] transform(...) {// 这里才是真正的“动作”}});
}
premain的主要作用是注册一个或多个ClassFileTransformer,但是这些transformer不会立刻执行,而是在之后类被加载的时候才会触发
注册了这些transformer之后,类的加载过程就变成了:
1.从class文件读取原始字节码
2.调用所有已注册的ClassFileTransformer方法
3.如果任何一个transformer返回了新的字节码数据,JVM就使用这个新的字节码去定义类(defineClass)
4.类加载完成之后,后续都使用的是修改后的版本
需要注意的一点是,这个修改之后的字节码文件版本只有在这次JVM运行期间是有效的,如果重新启动JVM的话,没有使用这个agent还是加载的原始的class文件
如果想永久修改字节码的话,在ClassFileTransformer中,把返回的byte[]写入文件即可
agentmain
对于JVM已经加载的类,可以通过Instrumentation.retransformClasses(Class<?>... classes)让 JVM 对已经加载的类重新应用 ClassFileTransformer
不会卸载原来的类并重新加载,而是重新应用transformer,生成新的字节码并替换当前类的定义
inst.retransformClasses(loader.loadClass("com.example.HelloWorld"));
可以实现:在不停止JVM的情况下更新类的行为
也可以直接替换类的字节码:
byte[] newByteCode = ...; // 新的字节码
inst.redefineClasses(new ClassDefinition(MyClass.class, newByteCode));