2015-04-06 28 views
1

我試圖修改在超類中聲明的方法CtMethod#insertBefore。但是,Javassist似乎不可能。修改Javassist在超類中聲明的方法

private class AbstractTestDataSource { 
    public Connection getConnection() throws SQLException { 
     return connection; 
    } 
} 

private class TestDataSource extends AbstractTestDataSource implements DataSource { 
    public Connection getConnection(String username, String password) throws SQLException { 
     return connection; 
    } 
    // other methods omitted 
} 

這是我ClassFileTransformer

public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, 
            ProtectionDomain protectionDomain, byte[] classfileBuffer) 
     throws Exception { 
    if (!className.equals("org/example/TestDataSource")) { 
     return classfileBuffer; 
    } 
    final CtClass ctClass = createCtClass(loader, classfileBuffer); 
    for (CtMethod method : ctClass.getMethods()) { 
     if (method.getName().equals("getConnection")) { 
      System.out.print(method.getName()); 
      System.out.println(method.getSignature()); 
      method.insertBefore("System.out.println(\"foo\");"); 
     } 
    } 
    return ctClass.toBytecode(); 
} 

當我打電話getConnection(String, String)方法,foo被打印到控制檯,但如果我認爲這種聲明在AbstractTestDataSource沒有任何反應getConnection()方法。 我在做什麼錯?

編輯

我可以證實,這兩種方法都儀器,因爲這是被打印到控制檯:

getConnection(Ljava/lang/String;Ljava/lang/String;)Ljava/sql/Connection; 
getConnection()Ljava/sql/Connection; 
+0

你已經確定insertBefore被調用了兩次,但你沒有檢查它實際被調用的是什麼。嘗試打印方法名稱和描述符。更好的是,看看你是否可以獲得目標類的實際類文件並反彙編它。 – Antimony 2015-04-06 14:34:59

+0

好吧,我想我知道問題在哪裏。超類沒有被修改,因爲在transform方法中,我只能轉換當前加載的一個類。我想我現在的選擇是重新轉換超類或動態覆蓋超類方法。現在嘗試後一種選擇... – Felix 2015-04-06 14:54:54

回答

1

我的解決方案是檢查getConnection方法是否在除當前類之外的類中聲明。

if (!ctClass.equals(method.getDeclaringClass())) { 
    method = overrideMethod(ctClass, method); 
} 

如果是這樣,創建(因而覆蓋)getConnection方法和代表對超類。

private CtMethod overrideMethod(CtClass ctClass, CtMethod getConnectionMethodOfSuperclass) 
     throws NotFoundException, CannotCompileException { 
    final CtMethod m = CtNewMethod.delegator(getConnectionMethodOfSuperclass, ctClass); 
    ctClass.addMethod(m); 
    return m; 
} 

它不覺得是理想的解決方案,但它工作正常。

+0

我仍然沒有看到你的原代碼是不是工作。根據[Javassist文檔](http://www.csg.ci.i.u-tokyo.ac.jp/~chiba/javassist/tutorial/tutorial2.html),您正在修改超類的方法。 「請注意,如果某個方法是從超類繼承的,則表示繼承方法的同一CtMethod對象表示在該超類中聲明的方法,CtMethod對象對應於每個方法聲明。 – Anssssss 2015-04-06 18:05:07

+0

也許任何負責該項目的人都能洞察它(也許這是一個錯誤)。我看到它的這個Jira頁面:https://issues.jboss.org/browse/JASSIST – Anssssss 2015-04-06 18:15:33

+1

問題是,我使用'-javaagent'內的javassist來在類加載時修改類。一個javaagent可以被看作是一個過濾器,每當一個類被加載時執行。相關的方法是'ClassFileTransformer'中的'public byte [] transform(..)'。在該方法中,您可以通過返回一個修改過的byte []來修改當前加載的類(只有該類)的'byte []'。這就是爲什麼對其他類(即使是超類)的任何修改都不適用。 – Felix 2015-04-06 18:43:14

0

你是不是在超類中重寫方法的getConnection()該方法有此方法簽名:

public Connection getConnection() throws SQLException 

在您的子類中,您必須重寫此方法使用相同的方法簽名,否則你只是在子類中創建一個新方法

+0

我想你誤解了我。我想修改'getConnection()'和'getConnection(String,String)',但它似乎只能修改在子類中聲明的方法而不是超類。 – Felix 2015-04-06 11:27:01