2016-03-13 55 views
1

不同來源提供實現ClassFileTransformer用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 
} 

這是最正確的方式,爲什麼?還有其他解決方案比那三個更好嗎?

https://stackoverflow.com/a/26725215/776884提到設置正確的類加載器。使用Instrumentation API時需要嗎? ClassPool classPool = new ClassPool()CtClass.makeClass()應與instumentation API一起使用嗎?

回答

1

所有的例子都是錯誤的,並不會在一般的設置工作。你永遠不應該使用默認的類池,你千萬別 - 比如在New Relic的博客顯示 - 共享的變壓器,因爲你不能告訴我們,如果加載的類由它們的類加載器之間的相關類池。

考慮過的位置的應用程序都有自己的類加載器的應用服務器;你甚至無法看到使用默認的類池(引用系統類加載器,即類路徑),將轉化類,你不能保證在應用服務器上的所有應用程序運行一些類的同一個版本,如果它們都包含其中。

唯一正確的解決辦法是:

ClassPool classPool = new ClassPool(); 
classPool.appendClassPath(new LoaderClassPath(loader)); 
CtClass ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer)); 

這樣你考慮到每一個類是由不同的類加載器,可以代表不同的觀點,即加載含有不同類別或不同版本的類而不是類路徑,您仍然解析提供的字節數組,該數組可以包含由其他Java代理先前觸發的變換器添加的新成員。

+0

感謝。在完全理解用於代碼生成的類池之前,我必須嘗試使用​​'ClassPool classPool = new ClassPool();'。獲取有關缺少Object類的錯誤有助於理解這一點。 – jikuja