2017-02-20 22 views
2

Мы начали с одной баночке с одним классом с одним из методов, как:Изменение только результат метода и загрузчик класса NoSuchMethod

boolean foo(int bar) { ... } 

Однако результат этого метода было бесполезно (на самом деле, всегда верно), и клиенты, использовал этот результат, чтобы что-то не получилось с ошибкой. По этой причине метод был изменен на:

void foo(int bar) { ... } 

и все детали перекомпилированы. Таким образом, мы можем взять на себя все пользователи этого банка вызывает метод, как:

foo(14); 

никто не использует форму (и выходит за рамки данного вопроса, если есть кто-то):

boolean x = foo(14); 

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

Проблема была в том, что в целевых системах, новая банка загружается без обновления клиентов. Не обновляемые клиенты терпят неудачу с исключением «NoSuchMethod» при поиске метода «foo» с результатом «boolean». То есть:

  • У клиента есть статут типа "foo (14);" без использования результата метода
  • клиент скомпилирован с использованием jar с булевым методом.
  • клиента и библиотека загружаются в целевой системе
  • библиотеки обновляются с новой баночкой с ничтожным методом
  • клиентских аварий с «NoSuchMethod»

Происхождения проблемы, кажется, что оба, Java, и C/C++ не допускает, чтобы два метода отличались только результатом, но только Java «name mangling» включает в себя тип результата в имени, которое ищет загрузчик классов (компоновщик в C/C++).

Вопрос: можно ли каким-либо образом обмануть библиотеку или загрузчик классов, чтобы пропустить исключение «NoSuchMethod» в этом сценарии?

ответ

1

Только мои мысли.

Вы можете попытаться изменить байт-код класса после фактической компиляции, используя special tools в качестве исправления. В байтекоде такие методы могут сосуществовать.

3

Такое поведение вполне ожидаемо. Это связано с тем, что скомпилированные классы клиента содержат constant pool, который включает в себя символическую ссылку на метод.

Давайте предположим, что у нас есть класс FooClass с двумя методами:

public boolean foo1(int bar) { 
    return true; 
} 

public void foo2(int bar) { 
    // ... 
} 

И давайте предположим, что у нас есть еще один класс Main, где мы вызываем оба метода:

FooClass fc = new FooClass(); 
    fc.foo1(1); 
    fc.foo2(1); 

Если мы будем разбирать Main.class мы увидим эти ссылки метода отличаются не только их именами, но и возвратом (уведомление (I) Z и (I) V):

 7: astore_1 
    8: aload_1 
    9: iconst_1 
    10: invokevirtual #4     // Method q42340444/FooClass.foo1:(I)Z 
    13: pop 
    14: aload_1 
    15: iconst_1 
    16: invokevirtual #5     // Method q42340444/FooClass.foo2:(I)V 

Где постоянный пул содержит:

#4 = Methodref   #2.#25   // q42340444/FooClass.foo1:(I)Z 
    #5 = Methodref   #2.#26   // q42340444/FooClass.foo2:(I)V 

Другими словами, он сохраняет оригинальный тип возвращаемого и выглядит в связанных библиотек для точно таким же способом, что вызывает NoSuchMethod исключение в вашем случае.

В частности, скомпилированные классы пользователей связаны с методом FooClass.foo:(I)Z, а ваша новая библиотека не содержит метода с таким описанием, а только FooClass.foo:(I)V.

Заключение: Вы не можете решить эту проблему без сохранения старого метода подписи (может быть, лучше аннотировать этот метод с @Deprecated аннотацию) или перекомпиляции кода клиента.

+0

Да, это как вы описали, но ваше предложение решить текущую реальную проблему? –

+0

Вы не можете решить проблему, не сохраняя старую подпись метода или перекомпилируя код клиента. – Andremoniy

+0

К сожалению, я не могу «сохранить» более старый метод и более новый, потому что Java не позволяет использовать два метода только в типе результата. –