Изменить метод, используя аннотации - PullRequest
12 голосов
/ 31 января 2011

Как я могу изменить то, что метод делает в Java?

Я имею в виду, я пытаюсь использовать аннотации для создания следующего кода

@Anno1(Argument = "Option1")
public class TestClass
{       
    @Anno2
    public void test()
    {
    }

}

Into

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 ]

12 голосов
/ 31 января 2011

Обработка аннотаций - неправильный путь для вас, из Википедия :

Когда исходный код Java скомпилирован, аннотации могут быть обработаны плагины компилятора, называемые аннотацией процессоры. Процессоры могут производить информационные сообщения или создать дополнительные исходные файлы Java или ресурсы, которые в свою очередь могут быть составлено и обработано, но аннотация процессоры не могут изменять аннотированный сам код.

Люди предложили вам правильный путь - АОП. В частности, вы можете использовать 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 автоматически компилирует для вас код, поэтому вам не нужно выполнять какую-либо ручную работу, надеюсь, это то, что вы назвали "магией";)

UPDATE:

если ваш код в методе 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 - специальная ссылочная переменная.

UPDATE2:

Если вы хотите добавить System.out.println( this ) в своем аспекте, вам нужно написать туда System.out.println( thisJoinPoint.getThis() ), только что протестировано, и оно работает. thisJoinPoint.getThis() возвращает вам «это», но не совсем; на самом деле это переменная объекта, и если вы хотите получить какую-либо собственность, вам нужно либо привести в действие, либо использовать отражение. И thisJoinPoint.getThis() не предоставляет доступа к частной собственности.

Что ж, теперь кажется, что на ваш вопрос ответили, но если я что-то пропустил, или у вас возникли дополнительные вопросы / проблемы с этим - не стесняйтесь спрашивать;)

3 голосов
/ 20 сентября 2015

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

Есть документ, объясняющий процедуру: Руководство хакера по Javac .

Примечательно, что это используется Project Lombok для автоматической генерации геттеров / сеттеров (среди прочего). следующая статья объясняет, как это происходит, в основном повторяя сказанное выше.

1 голос
/ 31 января 2011

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

Ваши аннотации очень похожи на концепцию pointcut (они отмечают место, где необходимо вставить код), а вставленный код близок к концепции advice .

Другим подходом будет синтаксический анализ исходного файла java в абстрактном синтаксическом дереве, изменение этого AST и сериализация во входные данные компилятора java.

1 голос
/ 31 января 2011

Хорошо, вы можете увидеть, будет ли полезен следующий шаблонный код:

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 .

0 голосов
/ 31 января 2011

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

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