2015-04-06 5 views
1

Я пытаюсь изменить метод с CtMethod#insertBefore, который объявлен в суперклассе. Однако с Джавасистом это кажется невозможным.Измените метод, объявленный в суперклассе с помощью 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 выводится на консоль, но если я называю getConnection() метод, объявленный в AbstractTestDataSource ничего не происходит. Что я делаю неправильно?

Редактировать

Я могу подтвердить, что оба метода инструментальными, потому что это то, что выводится на консоль:

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

Вы определили, что insertBefore вызывается дважды, но вы не проверили, на что он фактически вызван. Попробуйте распечатать имя и дескриптор метода. Еще лучше, посмотрите, можете ли вы получить фактический файл класса для целевого класса и разобрать его. – Antimony

+0

Хорошо, я думаю, я знаю, где проблема. Суперкласс не изменяется, потому что в методе преобразования я могу преобразовать только один класс, который в настоящее время загружен. Я думаю, что теперь мои варианты - либо перестроить суперкласс, либо динамически переписать метод суперкласса. Попробовав последний вариант сейчас ... – Felix

ответ

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 documentation] (http://www.csg.ci.i.u-tokyo.ac.jp/~chiba/javassist/tutorial/tutorial2.html), вы модифицировали метод суперкласса. «Обратите внимание, что если метод наследуется от суперкласса, то тот же объект CtMethod, который представляет унаследованный метод, представляет метод, объявленный в этом суперклассе. Объект CtMethod соответствует каждому объявлению метода». – Anssssss

+0

Может быть, тот, кто отвечает за проект, будет иметь представление об этом (возможно, это ошибка). Я вижу эту страницу Jira для нее: https://issues.jboss.org/browse/JASSIST – Anssssss

+1

Проблема в том, что я использую javassist внутри '-javaagent' для изменения классов во время загрузки класса. Javaagent можно рассматривать как фильтр, который выполняется всякий раз, когда загружается класс. Соответствующим методом является 'public byte [] transform (..)' в 'ClassFileTransformer'.Внутри этого метода вы можете изменить 'byte []' текущего загруженного класса (и только этого класса), возвращая измененный 'byte []'. Вот почему любые модификации других классов (даже суперкласса) не применяются. – Felix

0

Вы не перекрывая метод GetConnection() в родительском классе, этот метод имеет этот метод подписи:

public Connection getConnection() throws SQLException 

В вашем классе ребенка вам придется переопределить этот метод с тем же сигнатурой метода, иначе вы просто создаете новый метод в дочернем классе

+0

Я думаю, вы меня неправильно поняли. Я хочу изменить как 'getConnection()', так и 'getConnection (String, String)', однако возможно только изменение методов, объявленных в дочернем классе, а не в суперклассе. – Felix

 Смежные вопросы

  • Нет связанных вопросов^_^