Статьи

Обработка аннотаций Java с помощью отражения

В моей предыдущей статье, посвященной аннотациям Java , я изложил недавний пример использования и предоставил вам несколько примеров пользовательских аннотаций и их использование.

В этой статье я собираюсь сделать еще один шаг и дать вам несколько примеров пользовательских аннотаций и того, как вы будете обрабатывать эти пользовательские аннотации с помощью Java Reflection API. Изучив этот учебник, вы должны лучше понять простоту и гибкость, которые могут предоставить пользовательские аннотации. Итак, давайте углубимся в код!

Пользовательские аннотации

Для сегодняшнего примера кода я создал три разные аннотации: аннотации DoItLikeThis , DoItLikeThat и DoItWithAWhiffleBallBat . Каждая аннотация нацелена на отдельный тип элемента и имеет немного разные свойства, чтобы я мог показать вам, как искать и обрабатывать их соответствующим образом.

DoItLikeThis Annotation

Аннотация DoItLikeThis предназначена для ТИПА ElementType, что делает его доступным только для типов Java. Эта аннотация содержит три необязательных элемента description, action и логическое поле shouldDoItLikeThis. Если вы не предоставите никаких значений для этих элементов при использовании этой аннотации, они по умолчанию будут иметь указанные значения.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.keyhole.jonny.blog.annotations;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
/**
 * Annotation created for doing it like this.
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DoItLikeThis {
 
    /**
     * @return - The description.
     */
    String description() default "";
 
    /**
     * @return - The action.
     */
    String action() default "";
 
    /**
     * @return - Should we be doing it like this.
     */
    boolean shouldDoItLikeThis() default false;
 
}

DoItLikeThat Annotation

Аннотация DoItLikeThat — это аннотация, предназначенная только для полей Java. Эта аннотация также имеет похожий логический элемент с именем shouldDoItLikeThat, который не указывает значение по умолчанию и поэтому является обязательным элементом при использовании аннотации. Аннотация также содержит элемент, определенный как массив строк, который будет содержать список ролей пользователей, которые должны быть проверены.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.keyhole.jonny.blog.annotations;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
/**
 * Annotation created for doing it like that
 * instead of like this.
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DoItLikeThat {
 
    /**
     * @return - Should we be doing it like that.
     */
    boolean shouldDoItLikeThat();
 
    /**
     * @return - List of user roles that can do it like that.
     */
    String[] roles() default{};
 
}

DoItWithAWhiffleBallBat Annotation

Аннотация DoItWithAWhiffleBallBat предназначена для использования только с методами и аналогична другим аннотациям. Он также имеет похожий логический элемент, этот элемент должен называться shouldDotWithAWhiffleBallBat. Существует также еще один определенный элемент, который использует перечисление WhiffleBallBat, определяющее различные типы битовых шариков, которые доступны для использования, по умолчанию — классическая желтая классическая шариковая летучая мышь.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.keyhole.jonny.blog.annotations;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
/**
 * When you can't do it like this or do it like that,
 * do it with a whiffle ball bat.
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DoItWithAWhiffleBallBat {
 
    /**
     * @return - Should we be doing it with a whiffle ball bat.
     */
    boolean shouldDoItWithAWhiffleBallBat() default false;
 
    /**
     * @return - Sweet, which type of whiffle ball bat?
     */
    WhiffleBallBat batType() default WhiffleBallBat.YELLOW_PLASTIC;
 
}

Аннотированные классы

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

AnnotatedOne Class

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.keyhole.jonny.blog.annotations;
 
import java.util.Date;
 
@DoItLikeThis
public class AnnotatedOne implements AnnotatedClass {
 
    @DoItLikeThat(shouldDoItLikeThat = false)
    private String field1;
 
    @DoItLikeThat(shouldDoItLikeThat = true, roles = { "admin", "root" })
    private String field2;
 
    private String field3;
    private Date dateDoneLikeThis;
 
    /* setters and getters removed for brevity */
 
    @DoItWithAWhiffleBallBat(batType = WhiffleBallBat.BLACK_PLASTIC, shouldDoItWithAWhiffleBallBat = true)
    public void doWhateverItIs() {
        // method implementation
    }
 
    public void verifyIt() {
        // method implementation
    }
 
}

AnnotatedTwo Class

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.keyhole.jonny.blog.annotations;
 
import java.util.Date;
 
@DoItLikeThis(action = "PROCESS", shouldDoItLikeThis = true, description = "Class used for annotation example.")
public class AnnotatedTwo implements AnnotatedClass {
 
    @DoItLikeThat(shouldDoItLikeThat = true)
    private String field1;
 
    @DoItLikeThat(shouldDoItLikeThat = true, roles = { "web", "client" })
    private String field2;
 
    private String field3;
    private Date dateDoneLikeThis;
 
    /* setters and getters removed for brevity */
 
    @DoItWithAWhiffleBallBat(shouldDoItWithAWhiffleBallBat = true)
    public void doWhateverItIs() {
        // method implementation
    }
 
    public void verifyIt() {
        // method implementation
    }
 
}

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

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

Каждый из типов элементов Class, Field и Method реализует интерфейс AnnotatedElement, в котором определены следующие методы:

  • getAnnotations () — Возвращает все аннотации, присутствующие в этом элементе, включая любые, которые наследуются.
  • getDeclaredAnnotations () — Возвращает только аннотации, непосредственно представленные в этом элементе.
  • getAnnotation (Class <A> annotationClass) — возвращает аннотацию элемента для указанного типа аннотации, если не найдено, возвращает ноль.
  • isAnnotation () — возвращает true, если проверяемый элемент является аннотацией.
  • isAnnotationPresent (Class <? Extends Annotation> annotationClass) — Возвращает true, если указанная аннотация существует для проверяемого элемента.

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

1
2
3
4
if (ac.getClass().isAnnotationPresent(DoItLikeThis.class)) {
        // process the annotation, "ac" being the instance of the object we are inspecting
 
    }

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

1
2
3
4
DoItLikeThis anno = ac.getClass().getAnnotation(DoItLikeThis.class);
    System.out.println("Action: " + anno.action());
    System.out.println("Description: " + anno.description());
    System.out.println("DoItLikeThis:" + anno.shouldDoItLikeThis());

Для полей и методов проверка существующих аннотаций будет немного отличаться. Для этих типов элементов нам нужно пройти через все поля или методы, чтобы определить, существует ли аннотация на элементе. Вам нужно будет получить все поля или методы из класса, пройтись по массиву Field или Method, а затем определить, присутствует ли аннотация на элементе. Это должно выглядеть примерно так:

01
02
03
04
05
06
07
08
09
10
11
Field[] fields = ac.getClass().getDeclaredFields();
    for (Field field : fields) {
        if (field.isAnnotationPresent(DoItLikeThat.class)) {
            DoItLikeThat fAnno = field.getAnnotation(DoItLikeThat.class);
            System.out.println("Field: " + field.getName());
            System.out.println("DoItLikeThat:" + fAnno.shouldDoItLikeThat());
            for (String role : fAnno.roles()) {
                System.out.println("Role: " + role);
            }
        }
    }

Вывод

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

И, наконец, вот полный исходный код вместе с простым основным классом Java для выполнения кода:

AnnotatedClassProcessor

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.keyhole.jonny.blog.annotations;
 
import java.lang.reflect.Field;
import java.lang.reflect.Method;
 
public class AnnotatedClassProcessor {
 
    public void processClass(AnnotatedClass ac) {
        System.out.println("------Class Processing Begin---------");
 
        System.out.println("Class: " + ac.getClass().getName());
        if (ac.getClass().isAnnotationPresent(DoItLikeThis.class)) {
            // process the annotation, "ac" being the instance of the object we are inspecting
            DoItLikeThis anno = ac.getClass().getAnnotation(DoItLikeThis.class);
            System.out.println("Action: " + anno.action());
            System.out.println("Description: " + anno.description());
            System.out.println("DoItLikeThis:" + anno.shouldDoItLikeThis());
 
            System.out.println("------Field Processing---------");
            Field[] fields = ac.getClass().getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(DoItLikeThat.class)) {
                    DoItLikeThat fAnno = field.getAnnotation(DoItLikeThat.class);
                    System.out.println("Field: " + field.getName());
                    System.out.println("DoItLikeThat:" + fAnno.shouldDoItLikeThat());
                    for (String role : fAnno.roles()) {
                        System.out.println("Role: " + role);
                    }
                }
            }
 
            System.out.println("------Method Processing---------");
            Method[] methods = ac.getClass().getMethods();
            for (Method method : methods) {
                if ( method.isAnnotationPresent(DoItWithAWhiffleBallBat.class)) {
                    DoItWithAWhiffleBallBat mAnno = method.getAnnotation(DoItWithAWhiffleBallBat.class);
                    System.out.println("Use WhiffleBallBat? " + mAnno.shouldDoItWithAWhiffleBallBat());
                    System.out.println("Which WhiffleBallBat? " + mAnno.batType());
                }
            }
 
        }
        System.out.println("------Class Processing End---------");
    }
}

RunProcessor

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
package com.keyhole.jonny.blog.annotations;
 
public class RunProcessor {
 
    /**
     * @param args
     */
    public static void main(String[] args) {
 
        AnnotatedClassProcessor processor = new AnnotatedClassProcessor();
        processor.processClass(new AnnotatedOne());
        processor.processClass(new AnnotatedTwo());
 
    }
 
}
Ссылка: Обработка аннотаций Java с помощью Reflection от нашего партнера JCG Джонни Хакетта в блоге