Перейти к основному содержимому

Изменение параметров аннотации во время выполнения

· 4 мин. чтения

1. Обзор

Аннотации — форма метаданных, которую можно добавить в код Java. Эти аннотации могут быть обработаны во время компиляции и встроены в файлы классов или могут быть сохранены и доступны во время выполнения с помощью Reflection .

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

2. Аннотация

Java позволяет создавать новые аннотации , используя существующие. В простейшей форме аннотация представлена символом @ , за которым следует имя аннотации:

@Override

Давайте создадим собственную аннотацию Greeter :

@Retention(RetentionPolicy.RUNTIME)
public @interface Greeter {
public String greet() default "";
}

Теперь мы создадим класс Java Greetings , который использует аннотацию уровня класса :

@Greeter(greet="Good morning")
public class Greetings {}

Теперь мы получим доступ к значению аннотации, используя отражение. Класс Java Class предоставляет метод getAnnotation для доступа к аннотациям класса:

Greeter greetings = Greetings.class.getAnnotation(Greeter.class);
System.out.println("Hello there, " + greetings.greet() + " !!");

3. Изменить аннотацию

Класс класса Java поддерживает карту для управления аннотациями — класс аннотации в качестве ключей и объект аннотации в качестве значения:

Map<Class<? extends Annotation>, Annotation> map;

Мы обновим эту карту, чтобы изменить аннотацию во время выполнения. Подход к доступу к этой карте различается в разных реализациях JDK. Мы обсудим это для JDK7 и JDK8.

3.1. Реализация JDK 7

Класс Java Class имеет аннотации полей . Поскольку это приватное поле, чтобы получить к нему доступ, мы должны установить доступность поля в true . Java предоставляет метод getDeclaredField для доступа к любому полю по его имени: ``

Field annotations = Class.class.getDeclaredField(ANNOTATIONS);
annotations.setAccessible(true);

Теперь давайте получим доступ к карте аннотаций для класса Greeter :

Map<Class<? extends Annotation>, Annotation> map = annotations.get(targetClass);

Теперь это карта, которая содержит информацию обо всех аннотациях и их объектах-значениях. Мы хотим изменить значение аннотации Greeter , чего мы можем добиться, обновив объект аннотации класса Greeter :

map.put(targetAnnotation, targetValue);

3.2. Реализация JDK 8

Реализации Java 8 хранят информацию об аннотациях внутри класса AnnotationData . Мы можем получить доступ к этому объекту, используя метод annotationData . Мы установим доступность для метода annotationData в true , поскольку это закрытый метод:

Method method = Class.class.getDeclaredMethod(ANNOTATION_METHOD, null);
method.setAccessible(true);

Теперь мы можем получить доступ к полю аннотаций . Поскольку это поле также является частным полем, мы установим для доступа значение true :

Field annotations = annotationData.getClass().getDeclaredField(ANNOTATIONS);
annotations.setAccessible(true);

Это поле имеет карту кэша аннотаций, в которой хранятся класс аннотации и объект значения. Давайте изменим это:

Map<Class<? extends Annotation>, Annotation> map = annotations.get(annotationData); 
map.put(targetAnnotation, targetValue);

4. Применение

Возьмем этот пример:

Greeter greetings = Greetings.class.getAnnotation(Greeter.class);
System.err.println("Hello there, " + greetings.greet() + " !!");

Это будет приветствие «Доброе утро», так как это значение, которое мы указали для аннотации.

Теперь мы создадим еще один объект типа Greeter со значением «Добрый вечер»:

Greeter targetValue = new DynamicGreeter("Good evening");

Давайте обновим карту аннотаций новым значением:

alterAnnotationValueJDK8(Greetings.class, Greeter.class, targetValue);

Давайте снова проверим значение приветствия:

greetings = Greetings.class.getAnnotation(Greeter.class);
System.err.println("Hello there, " + greetings.greet() + " !!");

Он будет приветствовать как «Добрый вечер».

5. Вывод

Реализации Java используют два поля данных для хранения данных аннотации: аннотации , объявленные аннотации . Разница между этими двумя: сначала сохраняются аннотации из родительских классов, а затем сохраняются только для текущего класса.

Поскольку реализация getAnnotation различается в JDK 7 и JDK 8, для простоты здесь используется карта полей аннотаций .

И, как всегда, исходный код реализации доступен на Github .