Получение аннотаций во время выполнения с использованием рефлексии

Хотя аннотации спроектированы в основном для использования инструментами разработки и развертывания, если они специфицируют политику удержания RUNTIME, то могут быть опрошены во время выполнения любой Java-программой за счет использования рефлексии. Рефлексия — это средство, позволяющее получить информацию о классе во время выполнения программы. Программный интерфейс (API) рефлексии содержится в пакете java.lang.reflect. Существует множество способов применения рефлексии, и мы не будем здесь обсуждать их все. Тем не менее, мы пройдемся по нескольким примерам, имеющим отношение к аннотациям.

Первый шаг в использовании рефлексии — это получение объекта Class, представляющего класс, чью аннотацию нужно получить. Class — это один из встроенных классов Java, определенный в пакете j ava. lang. Он детально рассматривается во второй части книги. Есть разные способы получения объекта Class. Один из простейших — вызвать метод getClass () .определенный в Object. Его общая форма показана ниже:

final Class getClass ()

Он возвращает объект Class, который представляет вызывающий объект. (getClass () и несколько других связанных с рефлексией методов, используют средство обобщения. Однако поскольку обобщения не обсуждаются вплоть до главы 14, эти методы показаны и используются здесь в их "сырой" форме. В результате при компиляции следующих программ вы увидите предупреждающие сообщения. В главе 14 обобщения будут описаны со всех подробностях.)

После того, как вы получите объект Class, вы должны использовать его методы для получения информации о различных элементах, объявленных в классе, включая его аннотацию. Если вы хотите получить аннотации, ассоциированные с определенным элементом класса, вы должны сначала получить объект, представляющий этот элемент. Например, Class представляет (помимо прочих) методы getMethod (), getFieldO и getConstructor (), которые получают информацию о методе, поле и конструкторе соответственно. Эти методы возвращают объекты типов Method, Field и Constructor.

Чтобы понять этот процесс, рассмотрим пример, который получает аннотации, ассоциированные с методом. Чтобы сделать это, вы сначала получаете объект Class, представляющий класс, затем вызываете метод getMethod () этого объекта, указав имя метода. getMethod () имеет следующую общую форму:

Method getMethod(String methName, Class ... paramTypes)

Имя метода передается в methName. Если метод принимает аргументы, то объекты Class, представляющие их типы, также должны быть указаны в paramTypes. Обратите внимание, что paramTypes — это список аргументов переменной длины (varargs). Это означает, что вы можете специфицировать столько типов параметров, сколько нужно, включая ноль. getMethodO возвращает объект Method, который представляет метод. Если метод не может быть найден, возбуждается исключение NoSuchMethodException.

От объектов Class, Method, Field или Constructor вы можете получить специфические аннотации, ассоциированные с этим объектом, обратившись к getAnnotation (). Его общая форма представлена ниже:

Annotation getAnnotation(Class annoType)

Здесь annoType — это объект Class, представляющий аннотацию, в которой вы заинтересованы. Метод возвращает ссылку на аннотацию. Используя эту ссылку, вы можете получить значения, ассоциированные с членами аннотации.

Метод возвращает null, если аннотация не найдена; это случай, когда аннотация не имеет @Retention, установленную в RUNTIME.

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

import java.lang.annotation.*;
import java.lang.reflect.*; // Объявление типа аннотации.
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnno {
String str();
int val () ;
}
class Meta {
// Аннотировать метод.
@MyAnno(str = "Пример аннотации", val = 100)
public static void myMethO {
Meta ob = new Meta () ;
// Получить аннотацию из метода
//и отобразить значения членов.
try {
// Для начала получить Class,
// представляющий класс.
Class с = ob.getClass () ;
// Теперь получить объект Method,
// представляющий этот метод.
Method m = с.getMethod("myMeth") ;
// Далее получить аннотацию класса.
MyAnno anno = m.getAnnotation(MyAnno.class) ;v // Наконец, отобразить аннотацию.
System.out.println(anno.str() + " " + anno.val());
}
catch (NoSuchMethodException exc) {
System.out.println("Метод не найден.");
}
}
public static void main(String args[]) {
myMeth();
}
}

Вот как выглядит результат работы этой программы:

Пример аннотации 100

Эта программа использует рефлексию, как описано, чтобы получить и отобразить значения str и val в аннотации MyAnno, ассоциированной с myMeth () в классе Meta. Есть две вещи, на которые следует обратить особое внимание. Первая — выражение MyAnno. class в строке:

MyAnno anno = m.getAnnotation(MyAnno.class) ;

Это выражение вычисляется как объект Class типа MyAnno — аннотация. Это называется литералом класса. Вы можете использовать выражения этого типа всякий раз, когда требуется объект Class известного класса. Например, следующий оператор служит для получения объекта Class для Meta:

Class с = Meta.class;

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

Второй интересный момент — это способ, которым значения, ассоциированные с str и val, получаются, когда они выводятся в следующей строке:

System.out.println(anno.str() + " " + anno.valO);

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