Изменение метода с помощью аннотаций

как я могу изменить то, что метод делает в 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, который делегирует все вызовы исходным методам, кроме вызова для тестирования.