Статьи

Java аннотации: исследовано и объяснено

Одной из многих замечательных особенностей Java 5 SE является введение конструкции Annotations.
Аннотации — это теги, которые мы можем вставить в исходный код нашей программы, чтобы какой-то инструмент мог его обработать и придать ему смысл. Инструменты обработки аннотаций обычно используют API Reflection (Java 5 SE) для обработки кода на уровне исходного кода на уровне Java-кода или на уровне байт-кода для обработки файлов классов, в которые компилятор поместил аннотации. Java-аннотации прекрасно объясняются во многих местах в Интернете, но единственным местом, где я смог найти разумный и полный пример, была книга в твердом переплете Прентис Холл Публикации под названием Core Java: Том II — Расширенные функции, автор Cay S. Horstmann. и Гари Корнелл.

Почти все места в Интернете, которые пытаются объяснить аннотации, упускают самую важную часть, показывающую нам инструмент обработки аннотаций (APT) для наших пользовательских письменных аннотаций и способ его использования из нашего кода. Я использовал информацию из книги для создания некоторых аннотаций для проверки переменных и инициализации значений в них из файлов свойств для моего проекта. Мое наблюдение за отсутствием примеров над www для написания пользовательских аннотаций Java побудило меня написать эту статью. Итак, представляем вам образец пользовательской аннотации Java, чтобы помочь вам написать свои собственные аннотации для всего, что вы, возможно, делаете.

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

Объявление аннотации

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
package annotation.declaration;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
/**
 * Null Value Validate is, as the name suggests an annotation to
 * validate whether the parameter is null or not
 * @author         Y.Kamesh Rao
 *
 */
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
 
public @interface NullValueValidate {
    String paramName();
}

Обратите внимание на символ «@» (AT) перед ключевым словом «interface». Это синтаксис, используемый для объявления аннотации. Это называется интерфейсом аннотации . Методы интерфейса соответствуют элементам аннотации. paramName() — это единственный элемент, из которого состоит объявление аннотации. Он хранит имя аннотированного поля для отображения его в сообщении во время обработки. Обратите внимание, что объявление выглядит как объявление функции. Собственно, так оно и есть. @interface фактически объявляет интерфейс Java, реализация которого обеспечивается объектами, которые используют аннотацию. Обработчики аннотаций получают объекты, которые используют / реализуют аннотацию, и вызывают методы интерфейса аннотации для извлечения элементов аннотации. В нашем случае NullValueValidateAnnotationProcessor получит объект класса, в котором некоторые поля NullValueValidate аннотацией NullValueValidate . Затем этот процессор будет вызывать метод paramName() чтобы получить значение этого элемента аннотации.

Мы используем 3 из предоставленных Java аннотаций, чтобы аннотировать свойства нашего объявления. Альтернативно они называются встроенными аннотациями и используются для «аннотирования аннотаций» . (Ну, есть намного более жесткие скороговорки, чем это). @Documented — указывает, что объявление аннотации должно быть включено при создании документов для этого проекта с использованием JavaDocs . По умолчанию аннотации исключаются из документации, созданной с помощью команды javadocs . @Target — указывает целевые элементы в вашей Java-программе, к которым должна применяться аннотация. Это может быть либо Поле, Метод, Класс, либо весь Пакет. Наша аннотация NullValueValidate должна применяться только к полям классов. Вот возможные значения, принятые этим Enum —

  • ТИП — применяется только к типу. Тип может быть классом или интерфейсом Java или Enum или даже аннотацией.
  • FIELD — применяется только к полям Java (Objects, Instance или Static, объявленным на уровне класса).
  • МЕТОД — Применяется только к методам.
  • PARAMETER — применяется только к параметрам метода в определении метода.
  • КОНСТРУКТОР — Может применяться только к конструктору класса.
  • LOCAL_VARIABLE — может применяться только к локальным переменным. (Переменные, которые объявлены в методе или блоке кода).
  • ANNOTATION_TYPE — применяется только к типам аннотаций.
  • УПАКОВКА — применимо только к пакету.

@Retention — указывает политику хранения, которая будет использоваться для аннотации. Проще говоря, мы долго будем хранить аннотацию. Есть три возможных значения —

  • ИСТОЧНИК — аннотации должны быть отброшены компилятором.
  • CLASS — аннотации должны быть записаны в файле классов компилятором, но не должны сохраняться виртуальной машиной во время выполнения. Это поведение по умолчанию.
  • RUNTIME — аннотации должны быть записаны компилятором в файл класса и сохранены виртуальной машиной во время выполнения, чтобы их можно было читать рефлексивно.

Мы установили для RetentionPolicy значение RUNTIME поскольку планируем обрабатывать аннотации во время выполнения программы. @Target и @Retention также называются @Retention .

Инструмент обработки аннотаций

Инструмент обработки аннотаций анализирует получаемый объект и выполняет запрограммированные действия, чтобы найти аннотации, которые он обрабатывает, в проверяемом объекте. Вот процессор аннотаций для нашей ранее объявленной аннотации — NullValueValidate .

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package annotation.processor;
 
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import annotation.declaration.NullValueValidate;
 
/**
 * The class file to actually carry out the validations
 * for the various validate annotations we have declared
 * @author         Y.Kamesh Rao
 */
public class NullValueValidateAnnotationProcessor {
    /**
     * Method to process all the annotations
     * @param obj    The name of the object where
     *               annotations are to be identified and
     *               processed
     */
    public static void processAnnotations(Object obj) {
        try {
            Class cl = obj.getClass();
 
            // Checking all the fields for annotations
            for(Field f : cl.getDeclaredFields()) {
                // Since we are Validating fields, there may be many
                // NullPointer and similar exceptions thrown,
                // so we need  to catch them
                try {
                    // Processing all the annotations on a single field
                    for(Annotation a : f.getAnnotations()) {
                        // Checking for a NullValueValidate annotation
                        if(a.annotationType() == NullValueValidate.class) {
                            NullValueValidate nullVal = (NullValueValidate) a;
                            System.out.println('Processing the field : '+ nullVal.paramName());
 
                            // Setting the field to be accessible from our class
                            // is it is a private member of the class under processing
                            // (which its most likely going to be)
                            // The setAccessible method will not work if you have
                            // Java SecurityManager configured and active.
                            f.setAccessible(true);
 
                            // Checking the field for a null value and
                            // throwing an exception is a null value encountered.
                            // The get(Object obj) method on Field class returns the
                            // value of the Field for the Object which is under test right now.
                            // In other words, we need to send 'obj' as the object
                            // to this method since we are currently processing the
                            // annotations present on the 'obj' Object.
                            if(f.get(obj) == null) {
                                throw new NullPointerException('The value of the field '+f.toString()+' can't be NULL.');
                            } else
                                System.out.println('Value of the Object : '+f.get(obj));
                        }
                    }
                } catch(Exception e) {
                    System.out.println(e.getMessage());
                    e.printStackTrace();
                }
            }
        } catch(Exception e) {
            System.out.println(e.getMessage());
            e.printStackTrace();
        }
    }
}

Большая часть кода говорит сама за себя с комментариями. Пожалуйста, обратитесь к коду для подробного понимания того же. По сути, он имеет статический метод processAnnotations который принимает объект класса, который содержит аннотации, которые необходимо обработать. Затем мы используем Java Reflection API для обработки каждого поля в этом полученном параметре объекта и NullValueValidate необходимые действия по проверке нулевого значения всякий раз, когда находим аннотацию NullValueValidate в поле. Если найдено нулевое значение, мы NullPointerException или NullPointerException значение на консоль.

Использование аннотацииПожалуйста, обратитесь к следующему коду, который использует аннотацию NullValueValidate которую мы только что реализовали. Он также использует NullValueValidateAnnotationProcessor для обработки объявленных аннотаций в своем поле во время выполнения путем вызова его из конструктора. Также обратите внимание, что аннотации используются аналогичным образом, как модификаторы доступа, такие как private или public, с объявлениями переменных / полей. Обычно вводится новая строка для лучшей читаемости кода. Иначе, аннотация может очень хорошо существовать в той же строке, что и объявление переменной / поля. Название аннотации предшествует символ ‘@’ (AT).

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
package annotation;
 
import annotation.declaration.NullValueValidate;
import annotation.processor.NullValueValidateAnnotationProcessor;
 
/** Main class to test the Annotations 
 *   @author         Y.Kamesh Rao
 */
public class AnnotationExample {
    @NullValueValidate(paramName = 'testVar1') private String testVar1;
    @NullValueValidate(paramName = 'testVar2') private String testVar2;
 
 
    public AnnotationExample() {
        testVar2 = 'Testing the Null Value Validation...It Works...!';        
         
        // Calling the processor to process the annotations applied        
        // on this class object.        
        NullValueValidateAnnotationProcessor.processAnnotations(this);    
    }    
     
    public static void main(String args[]) {
        AnnotationExample ae = new AnnotationExample();    
    }
}

Выход

1
2
3
4
5
6
7
8
9
Processing the field:testVar1
Value of the Object:Testing the Null Value Validation...It Works...!
Processing the field:testVar2
The value of the field private java.lang.String annotation.AnnotationExample.testVar2 cannot be NULL.
java.lang.NullPointerException:The value of the field private java.lang.String annotation.AnnotationExample.testVar2 cannot be NULL.
        at annotation.processor.NullValueValidateAnnotationProcessor.processAnnotation
(NullValueValidateAnnotationProcessor.java:66)
        at annotation.AnnotationExample.(AnnotationExample.java:28)
        at annotation.AnnotationExample.main(AnnotationExample.java:33)

Вывод

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

Ссылка: Java Аннотации: исследовано и объяснено нашим партнером по JCG Й Камеш Рао в блоге OrangeApple .