Подтвердить что ты не робот

Измените метод, используя аннотации

Как я могу изменить метод, который используется в 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 не предоставляют никакого способа редактирования этого элемента (который в моем случае представляет собой метод).

Надеюсь, мой вопрос ясен и соответствует правилам. Если нет, прокомментируйте, и я уточню. Спасибо.

4b9b3361

Ответ 1

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

Когда исходный код 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() возвращает вас "this", но не точно; на самом деле это переменная Object, и если вы хотите получить какую-либо информацию, вам нужно либо бросить, либо использовать отражение. И thisJoinPoint.getThis() не предоставляет доступ к приватным свойствам.

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

Ответ 2

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

Вот документ, который объясняет процедуру: Руководство Hacker для Javac.

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

Ответ 3

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

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.

Ответ 4

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

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

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

Ответ 5

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