Добавление аннотаций во время сборки к геттеру Java, getX(), когда поле x аннотировано

Я хочу создать следующую аннотацию Java и обработать ее во время сборки:

@Target(value = FIELD)
interface @AnnotateGetter {
    Annotation[] value();
}

Если поле field снабжено аннотацией @AnnotateGetter, то все Annotations в массиве value добавляются к методу getField() того же класса, если такой метод существует.

Как это сделать проще всего?

  1. ApectJ, который может добавить аннотацию к методу с оператором объявления аннотации, но, хотя я знаю, как выбрать поле с аннотацией @AnnotateGetter, я не знаю, как выбрать метод, соответствующий аннотированному полю. с @AnnotateGetter
  2. Некоторая другая структура АОП
  3. Пишу свой javax.annotation.processing.Processor, который вызывает какую-то библиотеку, которая может добавить аннотацию к методу. Каковы наилучшие варианты для такой библиотеки? Должен ли он манипулировать байт-кодом после того, как javac скомпилировал исходный файл, или я могу каким-то образом подключиться к javac и добавить аннотации во время компиляции, прежде чем файл класса будет сгенерирован?
  4. что-то другое...

person XDR    schedule 14.04.2015    source источник
comment
Прежде всего, массивы Annotation не поддерживаются языком Java, поэтому вы не можете скомпилировать объявление класса выше.   -  person Andrey Breslav    schedule 14.04.2015
comment
Спасибо за информацию. Поскольку поддерживаются массивы определенных типов аннотаций, я просто ошибочно предположил, что массивы аннотаций будут работать. Я думаю, что я мог бы написать процессор для перемещения аннотаций, которые были применены непосредственно к полю, в его геттер, вместо того, чтобы оборачивать их в предложенную мной аннотацию. Есть ли проблемы с этим предложением? Если это сработает, я либо задам новый вопрос, либо перефразирую этот. Что рекомендуется?   -  person XDR    schedule 14.04.2015
comment
Я думаю, что простое перемещение аннотаций из поля в геттер должно работать (если они не ограничены аннотацией @Target)   -  person Andrey Breslav    schedule 16.04.2015
comment
Я приложил немало усилий к своему ответу. Как насчет обратной связи?   -  person kriegaex    schedule 01.05.2015


Ответы (2)


Вот решение, использующее APT (инструмент обработки аннотаций) через AspectJ. Он добавляет указанные аннотации к методам получения, но не удаляет их из полей. Так что это действие "копировать", а не "переместить".

Поддержка обработки аннотаций была добавлена ​​в AspectJ в версии 1.8.2 и описана в примечаниях к выпуску< /а>. Вот пример самосогласованного кода. Я скомпилировал его из командной строки, потому что из Eclipse мне не удалось запустить его в соответствии с сопровождающим AspectJ Описание Энди Клемента.

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

SO_AJ_APT_MoveAnnotationsFromMemberToGetter
    compile_run.bat
    src
        de/scrum_master/app/Person.java
    src_apt
        de/scrum_master/app/AnnotatedGetterProcessor.java
        de/scrum_master/app/AnnotateGetter.java
        de/scrum_master/app/CollationType.java
        de/scrum_master/app/SortOrder.java
        de/scrum_master/app/Unique.java
        META-INF/services/javax.annotation.processing.Processor

И src, и src_apt являются исходными каталогами, compile_run.bat – это пакетный файл Windows, создающий проект в два этапа (сначала обработчик аннотаций, затем остальная часть проекта) и запустить окончательный результат, чтобы доказать, что он действительно делает то, что должен.

Аннотации, которые будут использоваться для полей и позже копироваться в методы получения:

package de.scrum_master.app;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD })
public @interface Unique {}
package de.scrum_master.app;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD })
public @interface SortOrder {
    String value() default "ascending";
}
package de.scrum_master.app;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD })
public @interface CollationType {
    String value() default "alphabetical";
    String language() default "EN";
}

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

Обратите внимание, что эта мета-аннотация необходима только для обработки аннотаций и поэтому имеет область хранения SOURCE.

package de.scrum_master.app;

import java.lang.annotation.*;

@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface AnnotateGetter {
    Class<? extends Annotation>[] value();
}

Приложение для драйвера:

Что следует отметить:

  • Есть четыре поля с аннотациями (id, firstName, lastName, fieldWithoutGetter), но только первые три имеют соответствующие геттерные методы, последний — нет. Таким образом, мы ожидаем, что fieldWithoutGetter будет корректно обработан позже, либо пустой аспект ITD, либо не созданный позже с помощью APT.

  • Мета-аннотация @AnnotateGetter({ Unique.class, SortOrder.class, CollationType.class }) в классе Person указывает, какие аннотации следует учитывать для их копирования в методы получения. Позже вы можете поиграть с ним и посмотреть, как изменится результат, если вы удалите любой из них.

  • У нас также есть несколько фиктивных методов doSomething() и doSomethingElse(), на которые не должно влиять какое-либо копирование аннотаций позже, то есть они не должны получать новые аннотации через AspectJ. (Всегда хорошо иметь отрицательный тест.)

  • Метод main(..) использует отражение для печати всех полей и методов, включая их аннотации.

package de.scrum_master.app;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

@AnnotateGetter({ Unique.class, SortOrder.class, CollationType.class })
public class Person {
    @Unique
    private final int id;

    @SortOrder("descending")
    @CollationType("alphabetical")
    private final String firstName;

    @SortOrder("random")
    @CollationType(value = "alphanumeric", language = "DE")
    private final String lastName;

    @SortOrder("ascending")
    @CollationType(value = "numeric")
    private final int fieldWithoutGetter;

    public Person(int id, String firstName, String lastName, int fieldWithoutGetter) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
        this.fieldWithoutGetter = fieldWithoutGetter;
    }

    public int getId() { return id; }
    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }
    public void doSomething() {}
    public void doSomethingElse() {}

    public static void main(String[] args) {
        System.out.println("Field annotations:");
        for (Field field : Person.class.getDeclaredFields()) {
            System.out.println("  " + field.getName());
            for (Annotation annotation : field.getAnnotations())
                System.out.println("    " + annotation);
        }
        System.out.println();
        System.out.println("Method annotations:");
        for (Method method : Person.class.getDeclaredMethods()) {
            System.out.println("  " + method.getName());
            for (Annotation annotation : method.getAnnotations())
                System.out.println("    " + annotation);
        }
    }
}

Консольный вывод без APT + AspectJ:

Как видите, распечатываются аннотации полей, но не аннотации методов, потому что нам еще предстоит определить процессор аннотаций (см. далее ниже).

Field annotations:
  id
    @de.scrum_master.app.Unique()
  firstName
    @de.scrum_master.app.SortOrder(value=descending)
    @de.scrum_master.app.CollationType(value=alphabetical, language=EN)
  lastName
    @de.scrum_master.app.SortOrder(value=random)
    @de.scrum_master.app.CollationType(value=alphanumeric, language=DE)
  fieldWithoutGetter
    @de.scrum_master.app.SortOrder(value=ascending)
    @de.scrum_master.app.CollationType(value=numeric, language=EN)

Method annotations:
  main
  getId
  doSomething
  doSomethingElse
  getFirstName
  getLastName

Обработчик аннотаций:

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

package de.scrum_master.app;

public aspect AnnotateGetterAspect_Person_CollationType_lastName {
    declare @method : * Person.getLastName() : @de.scrum_master.app.CollationType(value = "alphanumeric", language = "DE");
}

Очень просто, не так ли? Обработчик аннотаций должен сгенерировать эти аспекты в каталог .apt_generated. Компилятор AspectJ сделает это за нас, как мы увидим позже. Но сначала вот процессор аннотаций (извините за длинный код, но это то, что вы просили):

package de.scrum_master.app;

import java.io.*;
import java.util.*;

import javax.tools.*;
import javax.annotation.processing.*;
import javax.lang.model.*;
import javax.lang.model.element.*;
import javax.lang.model.type.*;

@SupportedAnnotationTypes(value = { "*" })
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class AnnotatedGetterProcessor extends AbstractProcessor {
    private Filer filer;

    @Override
    public void init(ProcessingEnvironment env) {
        filer = env.getFiler();
    }

    @SuppressWarnings("unchecked")
    @Override
    public boolean process(
        Set<? extends TypeElement> elements,
        RoundEnvironment env
    ) {

        // Get classes annotated with something like: @AnnotateGetter({ Foo.class, Bar.class, Zot.class })
        env.getElementsAnnotatedWith(AnnotateGetter.class)
            .stream()
            .filter(annotatedClass -> annotatedClass.getKind() == ElementKind.CLASS)

            // For each filtered class, copy designated field annotations to corresponding getter method, if present
            .forEach(annotatedClass -> {
                String packageName = annotatedClass.getEnclosingElement().toString().substring(8);
                String className = annotatedClass.getSimpleName().toString();

                /*
                 * Unfortunately when we do something like this:
                 *   AnnotateGetter annotateGetter = annotatedClass.getAnnotation(AnnotateGetter.class);
                 *   Class<? extends Annotation> annotationToBeConverted = annotateGetter.value()[0];
                 * We will get this exception:
                 *   Internal compiler error:
                 *     javax.lang.model.type.MirroredTypesException:
                 *       Attempt to access Class objects for TypeMirrors
                 *       [de.scrum_master.app.Unique, de.scrum_master.app.SortOrder, de.scrum_master.app.CollationType]
                 *       at org.aspectj.org.eclipse.jdt.internal.compiler.apt.model.AnnotationMirrorImpl.getReflectionValue
                 *
                 * Thus, we have to use annotation mirrors instead of annotation classes directly,
                 * then tediously extracting annotation values from a nested data structure. :-(
                 */

                // Find @AnnotateGetter annotation and extract its array of values from deep within
                ((List<? extends AnnotationValue>) annotatedClass.getAnnotationMirrors()
                    .stream()
                    .filter(annotationMirror -> annotationMirror.getAnnotationType().toString().equals(AnnotateGetter.class.getName()))
                    .map(AnnotationMirror::getElementValues)
                    .map(Map::values)
                    .findFirst()
                    .get()
                    .stream()
                    .map(AnnotationValue::getValue)
                    .findFirst()
                    .get()
                )
                    .stream()
                    .map(annotationValueToBeCopied -> (TypeElement) ((DeclaredType) annotationValueToBeCopied.getValue()).asElement())
                    // For each annotation to be copied, get all correspondingly annotated fields
                    .forEach(annotationTypeElementToBeCopied -> {
                        env.getElementsAnnotatedWith(annotationTypeElementToBeCopied)
                            .stream()
                            .filter(annotatedField -> ((Element) annotatedField).getKind() == ElementKind.FIELD)
                            // For each annotated field create an ITD aspect
                            .forEach(annotatedField -> {
                                String fieldName = annotatedField.getSimpleName().toString();
                                String aspectName =
                                    "AnnotateGetterAspect_" + className + "_" +
                                    annotationTypeElementToBeCopied.getSimpleName() + "_" + fieldName;

                                StringBuilder annotationDeclaration = new StringBuilder()
                                    .append("@" + annotationTypeElementToBeCopied.getQualifiedName() + "(");

                                annotatedField.getAnnotationMirrors()
                                    .stream()
                                    .filter(annotationMirror -> annotationMirror.getAnnotationType().toString().equals(annotationTypeElementToBeCopied.getQualifiedName().toString()))
                                    .map(AnnotationMirror::getElementValues)
                                    .forEach(annotationParameters -> {
                                        annotationParameters.entrySet()
                                            .stream()
                                            .forEach(annotationParameter -> {
                                                ExecutableElement annotationParameterType = annotationParameter.getKey();
                                                AnnotationValue annotationParameterValue = annotationParameter.getValue();
                                                annotationDeclaration.append(annotationParameterType.getSimpleName() + " = ");
                                                if (annotationParameterType.getReturnType().toString().equals("java.lang.String"))
                                                    annotationDeclaration.append("\"" + annotationParameterValue + "\"");
                                                else
                                                    annotationDeclaration.append(annotationParameterValue);
                                                annotationDeclaration.append(", ");
                                            });
                                        if (!annotationParameters.entrySet().isEmpty())
                                            annotationDeclaration.setLength(annotationDeclaration.length() - 2);
                                        annotationDeclaration.append(")");
                                    });

                                // For each field with the current annotation, create an ITD aspect
                                // adding the same annotation to the member's getter method
                                String aspectSource = createAspectSource(
                                    annotatedClass, packageName, className,
                                    annotationDeclaration.toString(), fieldName, aspectName
                                );
                                writeAspectSourceToDisk(packageName, aspectName, aspectSource);
                            });
                    });
            });
        return true;
    }

    private String createAspectSource(
        Element parentElement,
        String packageName,
        String className,
        String annotationDeclaration,
        String fieldName,
        String aspectName
    ) {
        String getterMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);

        StringBuilder aspectSource = new StringBuilder()
            .append("package " + packageName + ";\n\n")
            .append("public aspect " + aspectName + " {\n");

        for (Element childElement : parentElement.getEnclosedElements()) {
            // Search for methods
            if (childElement.getKind() != ElementKind.METHOD)
                continue;
            ExecutableElement method = (ExecutableElement) childElement;

            // Search for correct getter method name
            if (!method.getSimpleName().toString().equals(getterMethodName))
                continue;
            // Parameter list for a getter method must be empty
            if (!method.getParameters().isEmpty())
                continue;
            // Getter method must be public
            if (!method.getModifiers().contains(Modifier.PUBLIC))
                continue;
            // Getter method must be non-static
            if (method.getModifiers().contains(Modifier.STATIC))
                continue;

            // Add call to found method
            aspectSource.append(
                "    declare @method : * " + className + "." + getterMethodName + "() : " +
                annotationDeclaration + ";\n"
            );
        }

        aspectSource.append("}\n");

        return aspectSource.toString();
    }

    private void writeAspectSourceToDisk(
        String packageName,
        String aspectName,
        String aspectSource
    ) {
        try {
            JavaFileObject file = filer.createSourceFile(packageName + "." + aspectName);
            file.openWriter().append(aspectSource).close();
            System.out.println("Generated aspect " + packageName + "." + aspectName);
        } catch (IOException ioe) {
            // Message "already created" can appear if processor runs more than once
            if (!ioe.getMessage().contains("already created"))
                ioe.printStackTrace();
        }
    }
}

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

src_apt/META-INF/services/javax.annotation.processing.Processor:

Этот файл нужен нам для дальнейшей работы обработчика аннотаций с компилятором AspectJ (ajc).

de.scrum_master.app.AnnotatedGetterProcessor

Пакетное создание файла и запуск проекта:

Извините, если это зависит от платформы, но я думаю, вы можете легко преобразовать его в сценарий оболочки UNIX/Linux, это довольно просто.

@echo off

set SRC_PATH=C:\Users\Alexander\Documents\java-src
set ASPECTJ_HOME=C:\Program Files\Java\AspectJ

echo Building annotation processor
cd "%SRC_PATH%\SO_AJ_APT_MoveAnnotationsFromMemberToGetter"
rmdir /s /q bin
del /q processor.jar
call "%ASPECTJ_HOME%\bin\ajc.bat" -1.8 -sourceroots src_apt -d bin -cp "%ASPECTJ_HOME%\lib\aspectjrt.jar"
jar -cvf processor.jar -C src_apt META-INF -C bin .

echo.
echo Generating aspects and building project
rmdir /s /q bin .apt_generated
call "%ASPECTJ_HOME%\bin\ajc.bat" -1.8 -sourceroots src -d bin -s .apt_generated -inpath processor.jar -cp "%ASPECTJ_HOME%\lib\aspectjrt.jar";processor.jar -showWeaveInfo

echo.
echo Running de.scrum_master.app.Person
java -cp bin;"%ASPECTJ_HOME%\lib\aspectjrt.jar" de.scrum_master.app.Person

Журнал консоли для процесса сборки и запуска:

Создание классов процессора и аннотаций, а затем их упаковка в файл processor.jar:

Building annotation processor
Manifest wurde hinzugefügt
Eintrag META-INF/ wird ignoriert
META-INF/services/ wird hinzugefügt(ein = 0) (aus = 0)(0 % gespeichert)
META-INF/services/javax.annotation.processing.Processor wird hinzugefügt(ein = 45) (aus = 46)(-2 % verkleinert)
de/ wird hinzugefügt(ein = 0) (aus = 0)(0 % gespeichert)
de/scrum_master/ wird hinzugefügt(ein = 0) (aus = 0)(0 % gespeichert)
de/scrum_master/app/ wird hinzugefügt(ein = 0) (aus = 0)(0 % gespeichert)
de/scrum_master/app/AnnotatedGetterProcessor.class wird hinzugefügt(ein = 8065) (aus = 3495)(56 % verkleinert)
de/scrum_master/app/AnnotateGetter.class wird hinzugefügt(ein = 508) (aus = 287)(43 % verkleinert)
de/scrum_master/app/CollationType.class wird hinzugefügt(ein = 520) (aus = 316)(39 % verkleinert)
de/scrum_master/app/SortOrder.class wird hinzugefügt(ein = 476) (aus = 296)(37 % verkleinert)
de/scrum_master/app/Unique.class wird hinzugefügt(ein = 398) (aus = 248)(37 % verkleinert)

Генерация аспекта + сборка проекта (выполняется всего одним вызовом компилятора AspectJ из-за его встроенной поддержки обработки аннотаций):

Generating aspects and building project
Generated aspect de.scrum_master.app.AnnotateGetterAspect_Person_Unique_id
Generated aspect de.scrum_master.app.AnnotateGetterAspect_Person_SortOrder_fieldWithoutGetter
Generated aspect de.scrum_master.app.AnnotateGetterAspect_Person_SortOrder_firstName
Generated aspect de.scrum_master.app.AnnotateGetterAspect_Person_SortOrder_lastName
Generated aspect de.scrum_master.app.AnnotateGetterAspect_Person_CollationType_fieldWithoutGetter
Generated aspect de.scrum_master.app.AnnotateGetterAspect_Person_CollationType_firstName
Generated aspect de.scrum_master.app.AnnotateGetterAspect_Person_CollationType_lastName
'public int de.scrum_master.app.Person.getId()' (Person.java:31) is annotated with @de.scrum_master.app.Unique method annotation from 'de.scrum_master.app.AnnotateGetterAspect_Person_Unique_id' (AnnotateGetterAspect_Person_Unique_id.java:4)

'public java.lang.String de.scrum_master.app.Person.getFirstName()' (Person.java:32) is annotated with @de.scrum_master.app.SortOrder method annotation from 'de.scrum_master.app.AnnotateGetterAspect_Person_SortOrder_firstName' (AnnotateGetterAspect_Person_SortOrder_firstName.java:4)

'public java.lang.String de.scrum_master.app.Person.getFirstName()' (Person.java:32) is annotated with @de.scrum_master.app.CollationType method annotation from 'de.scrum_master.app.AnnotateGetterAspect_Person_CollationType_firstName' (AnnotateGetterAspect_Person_CollationType_firstName.java:4)

'public java.lang.String de.scrum_master.app.Person.getLastName()' (Person.java:33) is annotated with @de.scrum_master.app.CollationType method annotation from 'de.scrum_master.app.AnnotateGetterAspect_Person_CollationType_lastName' (AnnotateGetterAspect_Person_CollationType_lastName.java:4)

'public java.lang.String de.scrum_master.app.Person.getLastName()' (Person.java:33) is annotated with @de.scrum_master.app.SortOrder method annotation from 'de.scrum_master.app.AnnotateGetterAspect_Person_SortOrder_lastName' (AnnotateGetterAspect_Person_SortOrder_lastName.java:4)

И последнее, но не менее важное: мы снова запускаем приложение драйвера. На этот раз мы должны увидеть аннотации, скопированные из аннотированных полей в соответствующие методы-геттеры (если такие методы существуют):

Running de.scrum_master.app.Person
Field annotations:
  id
    @de.scrum_master.app.Unique()
  firstName
    @de.scrum_master.app.SortOrder(value=descending)
    @de.scrum_master.app.CollationType(value=alphabetical, language=EN)
  lastName
    @de.scrum_master.app.SortOrder(value=random)
    @de.scrum_master.app.CollationType(value=alphanumeric, language=DE)
  fieldWithoutGetter
    @de.scrum_master.app.SortOrder(value=ascending)
    @de.scrum_master.app.CollationType(value=numeric, language=EN)

Method annotations:
  main
  getId
    @de.scrum_master.app.Unique()
  doSomethingElse
  getLastName
    @de.scrum_master.app.CollationType(value=alphanumeric, language=DE)
    @de.scrum_master.app.SortOrder(value=random)
  getFirstName
    @de.scrum_master.app.SortOrder(value=descending)
    @de.scrum_master.app.CollationType(value=alphabetical, language=EN)
  doSomething

Вуаля! Наслаждайтесь и не стесняйтесь задавать вопросы. :-)

Обновление (2015-05-03): Внимание, в моем коде процессора аннотаций я изначально забыл также скопировать значения параметров аннотаций, поэтому для каждой аннотации были созданы только значения по умолчанию. Я только что исправил это, сделав код процессора аннотаций еще более длинным. Поскольку я хотел сделать рефакторинг кода целесообразным и извлечь из него что-то, даже если вы уже приняли первоначальный ответ и все равно решили свою проблему по-другому, я поиграл с такими вещами Java 8, как лямбда-выражения, потоки, фильтры, карты. Это не особенно читабельно, если эта концепция для вас нова, особенно для вложенных циклов forEach, но я хотел попробовать и посмотреть, как далеко я смогу с этим продвинуться. ;-)

person kriegaex    schedule 26.04.2015
comment
Я решил свою рабочую проблему, не перемещая аннотации, и с тех пор я был завален работой, поэтому у меня не было времени взглянуть на это. На выходных прочитаю. Спасибо за подробный ответ. - person XDR; 01.05.2015
comment
Обратите внимание на мой обновленный код, который теперь копирует полную аннотацию, включая параметры. - person kriegaex; 03.05.2015

Для этого вы можете попробовать мою библиотеку Byte Buddy. Вы можете запустить его в процессе сборки при запуске приложения или даже из агента Java. Создание аннотации можно сделать следующим образом, используя последний выпуск:

DynamicType.Unloaded<?> type = new ByteBuddy()
  .makeAnnotation()
  .name("AnnotateGetter")
  .annotateType(new Target() {
    public ElementType value() { return ElementType.FIELD; }
    public Class<? extends Annotation> annotationType() { return Target.class; }
  }).defineMethod("value", 
                  SomeAnnotation.class, 
                  Collections.emptyList(),
                  Visibility.PUBLIC)
  .withoutCode()
  .make();

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

person Rafael Winterhalter    schedule 15.04.2015
comment
Есть ли способ удалить аннотацию из поля с помощью Byte Buddy? Я хочу переместить аннотации из поля в соответствующий геттер, для чего требуется 1) найти аннотации поля, 2) добавить их копии в соответствующий геттер, 3) удалить исходные аннотации из поля. Чтобы дать контекст, я хочу аннотировать свойства Groovy аннотациями Джексона, но мне нужно применить их к геттерам вместо полей из-за отложенной загрузки GORM. Groovy применяет аннотации свойств только к полям, поэтому я пишу процессор для перемещения аннотаций из полей в геттеры. - person XDR; 19.04.2015
comment
Сначала я напишу процессор, который перемещает аннотации из поля в метод (любой метод, но с геттером, сеттером и т. д., легко выбираемыми в качестве пунктов назначения), затем я напишу несколько аннотаций, указывающих, какие аннотации следует перемещать, и некоторые другие связанные настройки (например, как обрабатывать отсутствующие методы назначения: игнорировать, генерировать или выдавать исключение и т. д.). - person XDR; 19.04.2015
comment
Byte Buddy не предназначен для удаления вещей при переопределении класса. Таким образом, Byte Buddy обеспечивает бинарную совместимость. Что я бы вам порекомендовал: добавьте любые пользовательские аннотации к полям (которые вы можете легко найти), а затем, основываясь на этих аннотациях, добавьте фактические аннотации к соответствующим геттерам. Если этого недостаточно, Byte Buddy предоставляет базовый API ASM, который вы можете использовать для удаления. - person Rafael Winterhalter; 20.04.2015