不同的源提供了使用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
}
哪种方法最正确?为什么?还有其他解决方案比这三种解决方案更好吗?
https://stackoverflow.com/a/26725215/776884提到设置正确的类加载器。使用instrumentation API时是否需要? ClassPool classPool = new ClassPool()
与CtClass.makeClass()
的{{1}}是否应与实验API一起使用?
答案 0 :(得分:2)
所有示例都是错误的,无法在常规设置中使用。你永远不应该使用默认的类池,你永远不应该 - 例如在新的文物博客中显示 - 在变换器之间共享一个类池,因为你无法判断加载的类是否与它们的类加载器相关。
考虑应用服务器,其中app有自己的类加载器;您甚至无法使用默认类池(它引用系统类加载器,即类路径)来查看转换后的类,并且您不能保证应用程序服务器上的所有应用程序都运行相同版本的某些类(如果它们都包含它们)。
唯一正确的解决方案是:
ClassPool classPool = new ClassPool();
classPool.appendClassPath(new LoaderClassPath(loader));
CtClass ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));
这种方式考虑到每个类都由不同的类加载器加载,这个类加载器可以表示不同的视图,即包含不同类或类路径的不同版本的类,你仍然可以解析提供的字节数组包含由其他Java代理程序先前触发的转换器添加的新成员。