我使用asm庫來執行一些Java字節碼修改 - 特別是修改我的類來實現一個新的接口和相關的方法。我目前的做法是通過javaagent使用核心asm API。我想保留這種動態方法,而不是靜態修改.class文件。 在更高層次上,我的問題是,如果我選擇修改從B擴展的類A,我還需要修改B.(鑒於我對如何在JVM中加載類的理解,我相信類B將始終是(如果我錯了,請糾正我)假設我假設我需要返回並重新轉換B.我的方法是在這段代碼中捕獲的:
public byte[] transform(ClassLoader l, String name, Class<?> clazz, ProtectionDomain d, byte[] b) { throws IllegalClassFormatException { // **1** System.out.println("--->>> " + name); if (interestingClass(name)) { try { ClassReader cr = new ClassReader(b); ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); PyClassVisitorAdapter pv = new PyClassVisitorAdapter(cw, name); cr.accept(pv, 0); // **2** Retrieve the superclass and try to transform that if (! "Ljava/lang/Object;".equals(pv.getSuperName())) { String cName = classJvmToCanonical(pv.getSuperName()); Class[] classes = inst.getAllLoadedClasses(); for (Class c : classes) { if (c.getName().equals(cName)) { inst.retransformClasses(c); break; } } } // Dump the transformed class ClassReader cr2 = new ClassReader(cw.toByteArray()); ClassWriter cw2 = new ClassWriter(cr2, 0); TraceClassVisitor tcv = new TraceClassVisitor(cw2, new PrintWriter(System.out)); cr2.accept(tcv, 0); return cw2.toByteArray(); } catch (Exception ex) { ex.printStackTrace(); return null; } } else { return b; } }
(inst
是在構造函數中傳入的Instrumentation
的句柄) 我遇到困難的部分是用**2**
在注釋中標記的塊。
再說一遍,A擴展了B,並且我對變換A感興趣。我期待的是我會看到超類(B)的名稱被打印在**1**
上(但沒有變換,因為我沒有認為這在第一遍時很有趣),然后,一旦我到達**2**
並發現A的超類是B,我應該嘗試重新生成B.此時,我期待這個方法再次被調用(通過inst.retransformClasses()
)和我會看到B在**1**
上打印。但是,我沒有。 (我已經添加了打印語句,並且確定我正在進行轉換呼叫。我還檢查了Instrumentation.isRetransformClassesSupported()
和Instrumentation.isModifiableClass(c)
都返回true)。 我相信我已經正確地建立了代理;在清單中將
Can-Retransform-Classes和Can-Redefine-Classes設置為true。另外,當我在代理的premain
方法中將變壓器添加到Instrumentation時,我這樣做:
public static void premain(String agentArgs, Instrumentation inst) { inst.addTransformer(new PyClassFileTransformer(inst), true); }