2013-05-08 2 views
8

Вкратце: 1. У меня есть конечный класс, который я хочу создать для него динамический прокси. Как мне это сделать? 2. Могу ли я преобразовать MethodHandle в метод?Как создать динамический прокси для окончательного класса?

Прежде всего, существует ли какой-либо API для преобразования метода MethodHandle в метод? Нечто подобное в java.lang.invoke.MethodHandles

public MethodHandle unreflect(Method m) throws IllegalAccessException; 

но противоположным образом arrond?

Позвольте сказать, что я хочу создать динамический java.lang.reflect.Method. Он defiend в

public final 
    class Method extends AccessibleObject implements GenericDeclaration, 
               Member ; 

Так что, если я хочу использовать JDK динамического прокси я должен использовать некоторый интерфейс (член, например). Там, однако, есть 2 основных перетягивания. Во-первых, способ, такой как

public Class<?>[] getParameterTypes(); 

и такие, как

public Class<?> getReturnType(); 

не являются частью любого интерфейса, в то время как они широко используются.

Второй недостаток заключается в том, что он не может обеспечить замену. То есть я не могу передать свой динамический прокси код, ожидающий java.lang.reflect.Method.

Другой подход - использовать CGLIB или Javaassist. AFAIK, CGLIB не может проксировать последний класс, не так ли? Может ли выпускной класс прокси-сервера Javaassist? Как я могу «удалить» окончательный идентификатор из класса? AFAIL, Javvassist как-то может это сделать ...

ответ

0

К сожалению, то, что вы хотите, не представляется возможным:

Вы можете использовать CGLIB или Javassist создать прокси для конкретных классов, поскольку эти библиотеки динамически генерировать подкласс класса вы пытаетесь прокси. Класс final не может быть подклассом, поэтому вы не можете создать прокси таким образом.

PowerMock действительно позволяет вам прокси final классы и методы, но это потому, что он работает тесты под особый ClassLoader, который использует Javassist для изменения байткод классов, которые вы хотите, чтобы прокси-сервер, как они загружаются. (Вы не хотели бы использовать такие вещи в производстве, поскольку, как правило, модифицированная версия «зомби» этого класса не будет хороша для других целей, чем выполнение конкретного теста модульного модуля.)

Однако подход PowerMock не будет работать, но вы хотите прокси-сервер java.lang.reflect.Method, который находится на пути класса bootstrap, и поэтому загрузится перед любым типом инструмента PowerMock/Javassist и, следовательно, не будет прокси.

9

Это зависит от того, какой прокси-сервер вам нужен. В основном три вопроса о том, как вы можете достичь этого, из которых два возможны в производственном коде. Как указано в @probrekely, проблема cglib или javassist ist заключается в том, что они динамически создают подкласс, что невозможно для конечных классов. Вы можете избежать этого:

  • Отключить проверку байтового кода. Время выполнения Java проверяет байтовый код, чтобы обеспечить, чтобы не загружался вредоносный байтовый код.Это важно, например, для приема классов через сеть или Интернет, например апплет. Таким образом, вы можете создать подкласс конечного класса, так как верификатор байтового кода не остановит вас. Гипотетически, вы можете отключить эту проверку, если вы используете только доверенный код. Это можно сделать путем запуска:

    java -Xverify:none ApplicationName 
    

    Это, тем не менее, решение, которое я бы рекомендовал вам как минимум. Я бы не использовал этот aproach для производственного кода, но это, безусловно, самое простое решение для реализации.

  • Удалить модификатор final из загруженных классов до или после загрузки классов. Этого можно достичь, используя Java agent. Агент Java может быть установлен при запуске приложения через командную строку или во время выполнения через Attach API. С помощью инструмента байтового кода, такого как ASM, вы можете проанализировать исходный массив байтов и удалить окончательный модификатор из всех классов интересов. Также можно переопределить классы, которые уже были загружены. Удаление модификатора final не приводит к конфликтам со старыми версиями классов, так что такое переопределение всегда возможно.

  • Выполняйте то же, что я описал при удалении модификатора final, но переопределяю загруженный класс, чтобы фактически содержать всю вашу измерительную логику в исходном классе. Этот aporach будет наиболее требовательнее требовать больших усилий, но это сделает вашу аппаратуру трансверентной для всего другого кода. Это было бы самым чистым решением всех решений.

+0

Не могли бы вы рассказать подробнее о последнем варианте? – piotrek

+0

Вы можете переопределить классы с помощью API-интерфейса инструментария. Скажем, класс определяет метод foo. Вы можете переименовать панель методов и вызвать ее из вашего нового метода foo, который вы переопределили –

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