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 .