Изменение метода с помощью аннотаций
как я могу изменить то, что метод делает в Java ?
Я имею в виду, я пытаюсь использовать аннотации, чтобы сделать следующий код
@Anno1(Argument = "Option1")
public class TestClass
{
@Anno2
public void test()
{
}
}
на
public class TestClass
{
private static StaticReference z;
public void test()
{
z.invokeToAll();
}
}
это очень упрощенный пример того, что я пытаюсь сделать. Anno1
будет иметь много возможных комбинаций, но это пока не моя проблема. Моя проблема в том, как добавить код в метод test()
Я ищу более общее решение, если это возможно. Например. Добавлять каждую вид кода в методе (а не просто способ .invokeToAll()
)
пока я использую import javax.annotation.processing.*;
и у меня есть следующий код, но я не знаю как дальше оттуда
private void processMethodAnnotations(RoundEnvironment env)
{
for (Element e : env.getElementsAnnotatedWith(Anno2.class))
{
//If it is a valid annotation over a method
if (e.getKind() == ElementKind.METHOD)
{
//What to do here :S
}else
{
processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING,"Not a method!", e);
}
}
}
Я нашел что-то о Java Reflection, но я не нашел никакого источника, чтобы помочь мне в том, что я делаю.
Очевидно, Что Я extends AbstractProcessor
код
Я нашел этот учебник (http://www.zdnetasia.com/writing-and-processing-custom-annotations-part-3-39362483.htm) Но это касается создания нового класса, а не просто изменения метода. и javax.lang.model.elements
не предоставляйте никакого способа редактирования этого элемента (который в моем случае представляет собой метод).
Я надеюсь, что мой вопрос ясен и встроен в правила. Если нет, пожалуйста, прокомментируйте, и я уточню. Спасибо.
5 ответов
обработка аннотаций-неправильный путь для вас, от Википедия:
при компиляции исходного кода Java, аннотации могут обрабатываться Плагины компилятора, называемые аннотациями процессоры. Процессоры могут производить информационные сообщения или создание дополнительные исходные файлы Java или ресурсы, которые в свою очередь могут быть скомпилировано и обработано, но аннотации процессоры не могут изменять аннотированные сам код.
человек предложил вам правильный путь-AOP. В частности, вы можете использовать AspectJ. "Быстрый результат" (если вы используете Eclipse):
1) установить по ajdt (AspectJ Инструменты Разработки)
2) создайте проект AspectJ и добавьте туда свои классы и аннотации
3) Создать Аспект:
public aspect Processor {
private StaticReference z;
pointcut generic()
// intercept execution of method named test, annotated with @Anno1
// from any class type, annotated with @Anno2
: execution(@Anno2 * (@Anno1 *).test())
// method takes no arguments
&& args ();
// here you have write what you want method actually does
void around () : generic() {
z.invokeToAll();
}
}
теперь вы можете выполнить тест, и вы увидите, что он работает;) AJDT компилирует код для вас автоматически, поэтому не нужно никакой ручной работы, чтобы сделать, надеюсь, что это то, что вы назвали " магия" ;)
обновление:
если ваш код в методе test () зависит от значения аннотации Anno1, то внутри аспекта вы можете получить аннотацию класса, для которой она выполняется следующим образом:
void around () : generic() {
Annotation[] classAnnotations = thisJoinPoint.getThis().getClass().getAnnotations();
String ArgumentValue = null;
for ( Annotation annotation : classAnnotations ) {
if ( annotation instanceof Anno1 ) {
ArgumentValue = ((Anno1) annotation).Argument();
break;
}
}
if ( ArgumentValue != null && ArgumentValue.equals("Option1")) {
z.invokeToAll();
}
}
здесь thisJoinPoint является специальной ссылочной переменной.
обновление 2:
если вы хотите добавить System.out.println( this )
в вашем аспекте вам нужно написать там System.out.println( thisJoinPoint.getThis() )
, просто протестировано и работает. thisJoinPoint.getThis()
возвращает вам "это", но не совсем; на самом деле это переменная объекта, и если вы хотите получить какое-либо свойство, вам нужно либо бросить, либо использовать отражение. И thisJoinPoint.getThis()
не предоставляют доступ к частной собственности.
вполне возможно сделать то, что вы просите, хотя есть оговорка: полагаясь на частные API компилятора. Звучит страшно, но это не так (реализации компилятора, как правило, стабильны).
есть статья, которая объясняет процедуру:руководство хакера по Javac.
Примечательно, что это используется Проект Lombok обеспечить автоматическое поколение геттера / сеттера (среди прочего). The статье объясняет, как он делает это, в основном повторяя то, что сказано в вышеупомянутой статье.
Ну, вы можете увидеть, будет ли полезен следующий шаблонный код:
public void magic(Object bean, String[] args) throws Exception {
for (Method method : bean.getClass().getDeclaredMethods()) {
if (method.isAnnotationPresent(Anno2.class)) {
// Invoke the original method
method.invoke(bean, args);
// Invoke your 'z' method
StaticReference.invokeAll();
}
}
}
в качестве альтернативы вы можете использовать аспектно-ориентированное программирование, например, у вас есть в AspectJ.
Я вообще не уверен, можно ли изменить исходный или байтовый код с помощью аннотаций. Из того, что вы описываете, это выглядит как аспектно-ориентированное программирование может обеспечить решение вашей проблемы.
ваши аннотации довольно похожи на pointcut концепция (они отмечают место, где код должен быть вставлен), и вставленный код закрывает советы концепции.
другим подходом будет разбор исходный файл java в абстрактное дерево синтаксиса, измените этот AST и сериализуйте на вход компилятора java.
Если ваш класс расширяет подходящий интерфейс, вы можете обернуть его в DynamicProxy, который делегирует все вызовы исходным методам, кроме вызова для тестирования.