Поддержка обработчиков аннотаций в IDE NetBeans (часть II) Использование пользовательских обработчиков аннотаций в IDE

This tutorial needs a review. You can open a JIRA issue, or edit it in GitHub following these contribution guidelines.

_Составитель:, автор и редактор: Ирина Филиппова (Irina Filippova) _

  • Использование пользовательских обработчиков аннотаций в среде IDE

netbeans stamp 71 72 73
Figure 1. Содержимое этой страницы применимо к IDE NetBeans 7.0, 7.1, 7.2 и 7.3

В этом разделе учебного курса описываются способы добавления собственного обработчика особых аннотаций в проект в среде IDE. Написание обработчика не входит в круг задач данного учебного курса. Здесь описывается добавление его к проекту IDE NetBeans.

Образец приложения, используемый в этом приложении, был создан Джесси Глик (Jesse Glick) и опубликован как Часто задаваемые вопросы по вводу для предыдущих выпусков IDE.

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

Для работы с этим учебным курсом требуются программное обеспечение и ресурсы, перечисленные ниже.

Программное обеспечение или материал Требуемая версия

IDE NetBeans

7.0, 7.1, 7.2, 7.3

Комплект для разработчика на языке Java (JDK)

версия 6 или 7

lombok.jar

версия 1.12.4 и более поздние

Определение аннотации и создание обработчика аннотаций

В этом упражнении мы создадим проект библиотеки классов.

  1. Выберите File ("Файл") > New Project ("Создать проект") и выберите тип проекта Java Class Library ("Библиотека классов Java") в категории Java. Нажмите кнопку "Далее".

  2. Введите * AnnProcessor * в поле "Имя проекта" и укажите местоположение для проекта. Нажмите кнопку "Завершить".

При нажатии кнопки "Готово" среда IDE создаст проект библиотеки классов, который появится в окне Projects ("Проекты").

  1. Щелкните правой кнопкой мыши узел проекта AnnProcessor в окне Projects ("Проекты") и выберите Properties ("Свойства").

  2. В категории "Источники" подтвердите, что для JDK 6 или JDK 7 указан формат исходного кода/двоичный формат.

  3. Выберите вкладку "Библиотеки" и подтвердите, что платформой Java является JDK 1.6 или JDK 1.7. Нажмите кнопку "ОК", чтобы закрыть окно Project Properties ("Свойства проекта").

В этом упражнении мы создадим несколько пакетов Java и по одному классу Java в каждом из пакетов.

  1. В проекте 'AnnProcessor' щелкните правой кнопкой мыши узел 'Пакеты исходных кодов' и выберите 'Создать > Пакет Java'.

  2. Введите * ann * в поле "Имя пакета" и нажмите кнопку "Готово" для создания нового пакета Java.

  3. Повторите два предыдущих действия, чтобы создать пакет Java под названием * proc *.

После создания двух пакетов Java структура проекта должна быть подобной изображенной ниже.

packages
Figure 2. Структура проекта для обработчика аннотаций.
  1. Щелкните правой кнопкой мыши пакет Java ann и выберите New ("Создать") > Java class ("Класс Java").

  2. Введите * Handleable * в поле Class Name ("Имя класса"). Нажмите кнопку "Завершить".

  3. Измените файл Handleable.java , добавив приведенный ниже код. Сохраните файл.

package ann;

public *@interface* Handleable {

}

Так объявляются аннотации — совершенно аналогично объявлению интерфейса. Различием является то, что ключевому слову interface должен предшествовать знак @ . Эта аннотация именуется Handleable (обрабатываемой).

Дополнительные сведения. В объявлениях аннотаций можно указать дополнительные параметры, например, типы элементов, к которым можно добавить аннотации, например, классы или методы. Для этого добавляется @Target(value = {ElementType.TYPE}) для классов и @Target(value = {ElementType.METHOD}). Таким образом, объявление аннотаций становится автоматическим с помощью мета-аннотаций.

Теперь необходимо добавить к обработчику аннотаций код для обработки аннотации Handleable .

  1. Щелкните правой кнопкой мыши пакет* Java proc* и выберите New ("Создать") > Java class ("Класс Java").

  2. Введите * HandleableProcessor * в поле Class Name ("Имя класса"). Нажмите кнопку "Завершить".

  3. Измените класс HandleableProcessor.java , добавив нижеприведенный код. Сохраните изменения.

Примечание. Значение @SupportedSourceVersion (выделено жирным шрифтом) зависит от используемой версии JDK и может быть двух вариантов: (SourceVersion.RELEASE_7) или (SourceVersion.RELEASE_6) .

package proc;

import ann.Handleable;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;

@SupportedAnnotationTypes("ann.Handleable")
@SupportedSourceVersion(*SourceVersion.RELEASE_7*)
public class HandleableProcessor extends AbstractProcessor {

    /** public for ServiceLoader */
    public HandleableProcessor() {
    }

    public boolean process(Set<? extends TypeElement> annotations,
            RoundEnvironment roundEnv) {
        for (Element e : roundEnv.getElementsAnnotatedWith(Handleable.class)) {
            if (e.getKind() != ElementKind.FIELD) {
                processingEnv.getMessager().printMessage(
                        Diagnostic.Kind.WARNING,
                        "Not a field", e);
                continue;
            }
            String name = capitalize(e.getSimpleName().toString());
            TypeElement clazz = (TypeElement) e.getEnclosingElement();
            try {
                JavaFileObject f = processingEnv.getFiler().
                        createSourceFile(clazz.getQualifiedName() + "Extras");
                processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
                        "Creating " + f.toUri());
                Writer w = f.openWriter();
                try {
                    PrintWriter pw = new PrintWriter(w);
                    pw.println("package "
                            + clazz.getEnclosingElement().getSimpleName() + ";");
                    pw.println("public abstract class "
                            + clazz.getSimpleName() + "Extras {");
                    pw.println("    protected " + clazz.getSimpleName()
                            + "Extras() {}");
                    TypeMirror type = e.asType();
                    pw.println("    /** Handle something. */");
                    pw.println("    protected final void handle" + name
                            + "(" + type + " value) {");
                    pw.println("        System.out.println(value);");
                    pw.println("    }");
                    pw.println("}");
                    pw.flush();
                } finally {
                    w.close();
                }
            } catch (IOException x) {
                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
                        x.toString());
            }
        }
        return true;
    }

    private static String capitalize(String name) {
        char[] c = name.toCharArray();
        c[0] = Character.toUpperCase(c[0]);
        return new String(c);
    }
}

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

Сперва следует указать типы аннотаций, поддерживаемые обработчиком аннотаций (используя @SupportedAnnotationTypes ) и поддерживаемую версию исходных файлов (используя @SupportedSourceVersion ). В данном случае версией является JDK 6:

@SupportedAnnotationTypes("ann.Handleable")
@SupportedSourceVersion(SourceVersion.RELEASE_6)

Затем следует объявить общедоступный класс для обработчика, расширяющий класс AbstractProcessor из пакета javax.annotation.processing . AbstractProcessor является стандартным надклассом для обработчиков конкретных аннотаций и содержит необходимые методы для обработки аннотаций.

public class HandleableProcessor extends AbstractProcessor {
...
}

Теперь необходимо предоставить общедоступный конструктор для данного класса.

public class HandleableProcessor extends AbstractProcessor {
*    public HandleableProcessor() {
    }*
...

}

Затем следует вызвать метод process () родительского класса AbstractProcessor . Посредством этого метода предоставляются аннотации, доступные для обработки. Кроме того, этот метод содержит данные о цикле обработки.

public class HandleableProcessor extends AbstractProcessor {*
   *...
*     public boolean process(Set<? extends TypeElement> annotations,
            RoundEnvironment roundEnv) {
     ...
     }
*
}

Логика обработчика аннотаций содержится внутри метода process() класса AbstractProcessor . Обратите внимание, что при помощи класса AbstractProcessor также можно получить доступ к интерфейсу ProcessingEnvironment , позволяющему обработчикам аннотаций использовать несколько полезных функций, например средство для работы с файловой системой (обработчик файловой системы, позволяющий обработчикам аннотаций создавать файлы) и средство вывода сообщений (способ предупреждения об ошибках обработчиков аннотаций).

public class HandleableProcessor extends AbstractProcessor {*
   *...
     public boolean process(Set<? extends TypeElement> annotations,
            RoundEnvironment roundEnv) {//For each element annotated with the Handleable annotation
            *for (Element e : roundEnv.getElementsAnnotatedWith(Handleable.class)) {

*//Check if the type of the annotated element is not a field. If yes, return a warning*.
if (e.getKind() != ElementKind.FIELD) {
processingEnv.getMessager().printMessage(
Diagnostic.Kind.WARNING,
"Not a field", e);
continue;
}
            *//Define the following variables: name and clazz*.**
String name = capitalize(e.getSimpleName().toString());
TypeElement clazz = (TypeElement) e.getEnclosingElement();
*//Generate a source file with a specified class name. *
            try {
JavaFileObject f = processingEnv.getFiler().
createSourceFile(clazz.getQualifiedName() + "Extras");
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
"Creating " + f.toUri());
Writer w = f.openWriter();
*//Add the content to the newly generated file*.
                    try {
PrintWriter pw = new PrintWriter(w);
pw.println("package "
+ clazz.getEnclosingElement().getSimpleName() + ";");
pw.println("public abstract class "
+ clazz.getSimpleName() + "Extras {");
pw.println("    protected " + clazz.getSimpleName()
+ "Extras() {}");
TypeMirror type = e.asType();
pw.println("    /** Handle something. */");
pw.println("    protected final void handle" + name
+ "(" + type + " value) {");
pw.println("        System.out.println(value);");
pw.println("    }");
pw.println("}");
pw.flush();
} finally {
w.close();
}
} catch (IOException x) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
x.toString());
}
}*return true;
    * }*
...
}

В последнем блоке этого кода объявляется метод capitalize , используемый для написания имени аннотированного элемента с заглавной буквы.

public class HandleableProcessor extends AbstractProcessor {*
   *...*

  private static String capitalize(String name) {
char[] c = name.toCharArray();
c[0] = Character.toUpperCase(c[0]);
return new String(c);
}
*}
  1. Создайте проект, щелкнув правой кнопкой мыши проект AnnProcessor и выбрав 'Сборка'.

Работа с обработчиком аннотаций в среде IDE

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

  1. Выберите File ("Файл") > New Project ("Создать проект") и выберите тип проекта Java Application ("Приложение Java") в категории Java. Нажмите кнопку "Далее".

  2. На странице "Имя и расположение" введите * Demo * в поле "Имя проекта" и укажите расположение проекта.

  3. Введите * demo.Main * в поле Create Main Class ("Создать главный класс"). Нажмите кнопку "Завершить".

demo project wizard
Figure 3. Создание проекта Demo в мастере создания проектов.
  1. Откройте окно 'Свойства проекта' и убедитесь, что JDK 6 или JDK 7 выбран как двоичный формат/формат исходного кода на панели "Исходные коды", а также что JDK 1.6 or JDK 1.7 установлен как платформа Java на панели "Библиотеки".

  2. Измените класс Main.java , добавив приведенный ниже код. Сохраните изменения.

package demo;

*import ann.Handleable;*

public class Main *extends MainExtras* {

    *@Handleable
    private String stuff;*

    *public static void main(String[] args) {
        new Main().handleStuff("hello");
    }*
}

Данный код содержит следующие элементы:

  • оператор импорта для обработчика особых аннотаций ann.Handleable ;

  • общедоступный класс Main , расширяющий класс MainExtras ( MainExtras должен быть создан обработчиком аннотаций во время компиляции);

  • закрытое поле под названием stuff , с аннотацией @Handleable ;

  • метод main , вызывающий метод handleStuff , который объявляется в автоматически создаваемом классе MainExtras .

В этом простом примере метод handleStuff только распечатывает текущее значение. Назначение метода можно изменить.

После сохранения кода Main.java можно увидеть, что среда IDE сообщает о ряде ошибок компиляции. Это происходит, поскольку обработчик аннотаций еще не добавлен в проект.

  1. Щелкните правой кнопкой мыши узел проекта Demo в окне "Проекты", выберите "Свойства", затем выберите категорию "Библиотеки" в окне 'Свойства проекта'.

  2. На вкладке Compile ("Компиляция") щелкните Add Project ("Добавить проект") и найдите проект AnnProcessor .

demo properties compile
Figure 4. Вкладка 'Компилировать' в категории 'Библиотеки' окна свойств проекта

Вкладка "Компиляция" соответствует параметру -classpath компилятора Java. Поскольку обработчик аннотаций является единым файлом JAR, который содержит как определение аннотаций, так и обработчик аннотаций, его следует добавить к пути классов для проекта, которым является вкладка Compile ("Компиляция").

  1. Выберите категорию "Компиляция" в окне "Свойства проекта" и установите флажки "Разрешить обработку аннотаций" и "Разрешить обработку аннотаций в редакторе".

  2. Укажите, какой обработчик аннотаций должен быть запущен, нажав кнопку Add ("Добавить") рядом с текстовой областью обработчиков аннотаций и введя * proc.HandleableProcessor * в поле FQN ("Полностью определенное имя") обработчика аннотаций.

demo processor fqn
Figure 5. Диалоговое окно 'FQN обработчика аннотаций'

Категория Compiling ("Компиляция") в окне Project Properties ("Свойства проекта") должна выглядеть, как на приведенном ниже изображении.

demo properties compiling
Figure 6. Категория "Компиляция" в окне "Свойства проекта".
  1. Нажмите кнопку OK в окне Properties ("Свойства").

Примечание. В файле Main.java все еще могут отображаться ошибки компиляции. Это происходит, поскольку в среде IDE еще не определено местоположение файла MainExtras.java , в котором объявляется метод handleStuff . После первого создания проекта Demo будет создан файл MainExtras.java . Если для проекта включено режим Compile On Save ("Компилировать при сохранении"), среда IDE компилирует проект при сохранении Main.java .

  1. Щелкните правой кнопкой мыши проект Demo и выберите Build ("Сборка").

Если после сборки проекта взглянуть на него в окне Projects ("Проекты"), то можно будет увидеть новый узел Generated Sources с файлом demo/MainExtras.java .

demo generated sources
Figure 7. В окне 'Проекты' отображаются созданные источники

При просмотре содержимого созданного файла MainExtras.java можно увидеть, что обработчик аннотаций создал класс MainExtras с методом handleStuff . Метод handleStuff и является методом, вызываемым из аннотированного файла Main.java .

package demo;
public abstract class MainExtras {
    protected MainExtras() {}
    /** Handle something. */
    protected final void handleStuff(java.lang.String value) {
        System.out.println(value);
    }
}
  1. Щелкните правой кнопкой мыши проект Demo и выберите Run ("Запустить").

При щелчке Run ("Запустить") в окне вывода можно будет увидеть следующее. Выполняется компиляция проекта Demo, и на экран выводится сообщение.

demo run
Figure 8. В окне 'Проекты' отображаются созданные источники

Дополнительные сведения

Ознакомьтесь со следующими ресурсами для получения дополнительных сведений об аннотациях в приложениях Java: