使用 Javassist 的 ClassFileTransformer 实现
ClassFileTransformer implementations with Javassist
不同的来源提供了使用 Javassist 实现 ClassFileTransformer
的不同方法:
blog.newrelic.com/2014/09/29/diving-bytecode-manipulation-creating-audit-log-asm-javassist/
public byte[] transform(...) {
ClassPool pool = ClassPool.getDefault(); // Edited to simplify
pool.insertClassPath(new ByteArrayClassPath(className, classfileBuffer));
CtClass cclass = pool.get(className.replaceAll("/", "."));
...
return cclass.toBytecode();
}
blog.javabenchmark.org/2013/05/java-instrumentation-tutorial.html
public byte[] transform(...) {
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get("org.javabenchmark.instrumentation.Sleeping");
...
return cc.to:byteCode(); // edited to simplify
}
http://javapapers.com/core-java/java-instrumentation/
public byte[] transform(...) {
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));
...
return ctClass.to:byteCode(); // edited to simplify
}
哪种方法最正确,为什么?还有比这三个更好的解决方案吗?
提到设置正确的类加载器。使用 instrumentation 时是否需要 API? ClassPool classPool = new ClassPool()
和 CtClass.makeClass()
是否应该与仪器一起使用 API?
所有示例都是错误的,在一般设置中将不起作用。你永远不应该使用默认的 class 池,你永远不应该 - 如新遗迹博客中所示 - 在转换器之间共享一个 class 池,因为你无法判断加载的 class 是否相关通过他们的 class 加载程序。
考虑一个应用程序服务器,其中的应用程序都有自己的 class 加载程序;您甚至无法使用默认的 class 池(引用系统 class 加载程序,即 class 路径)看到转换后的 classes,并且您不能保证所有应用程序在应用程序服务器上 运行 某些 class 的相同版本,如果它们都包含它们。
唯一正确的解法是:
ClassPool classPool = new ClassPool();
classPool.appendClassPath(new LoaderClassPath(loader));
CtClass ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));
这样你就考虑到每个 class 都由不同的 class 加载器加载,它可以代表不同的视图,即包含不同的 classes 或不同版本的class 而不是 class 路径,您仍然解析提供的字节数组,该数组可以包含由其他 Java 代理先前触发的转换器添加的新成员。
不同的来源提供了使用 Javassist 实现 ClassFileTransformer
的不同方法:
blog.newrelic.com/2014/09/29/diving-bytecode-manipulation-creating-audit-log-asm-javassist/
public byte[] transform(...) {
ClassPool pool = ClassPool.getDefault(); // Edited to simplify
pool.insertClassPath(new ByteArrayClassPath(className, classfileBuffer));
CtClass cclass = pool.get(className.replaceAll("/", "."));
...
return cclass.toBytecode();
}
blog.javabenchmark.org/2013/05/java-instrumentation-tutorial.html
public byte[] transform(...) {
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get("org.javabenchmark.instrumentation.Sleeping");
...
return cc.to:byteCode(); // edited to simplify
}
http://javapapers.com/core-java/java-instrumentation/
public byte[] transform(...) {
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));
...
return ctClass.to:byteCode(); // edited to simplify
}
哪种方法最正确,为什么?还有比这三个更好的解决方案吗?
提到设置正确的类加载器。使用 instrumentation 时是否需要 API? ClassPool classPool = new ClassPool()
和 CtClass.makeClass()
是否应该与仪器一起使用 API?
所有示例都是错误的,在一般设置中将不起作用。你永远不应该使用默认的 class 池,你永远不应该 - 如新遗迹博客中所示 - 在转换器之间共享一个 class 池,因为你无法判断加载的 class 是否相关通过他们的 class 加载程序。
考虑一个应用程序服务器,其中的应用程序都有自己的 class 加载程序;您甚至无法使用默认的 class 池(引用系统 class 加载程序,即 class 路径)看到转换后的 classes,并且您不能保证所有应用程序在应用程序服务器上 运行 某些 class 的相同版本,如果它们都包含它们。
唯一正确的解法是:
ClassPool classPool = new ClassPool();
classPool.appendClassPath(new LoaderClassPath(loader));
CtClass ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));
这样你就考虑到每个 class 都由不同的 class 加载器加载,它可以代表不同的视图,即包含不同的 classes 或不同版本的class 而不是 class 路径,您仍然解析提供的字节数组,该数组可以包含由其他 Java 代理先前触发的转换器添加的新成员。