Поддержка обработчиков аннотаций в 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

В этом разделе учебного курса описываются способы добавления собственного обработчика особых аннотаций в проект в среде IDE. Написание обработчика не входит в круг задач данного учебного курса. Здесь описывается добавление его к проекту IDE NetBeans.
Образец приложения, используемый в этом приложении, был создан Джесси Глик (Jesse Glick) и опубликован как Часто задаваемые вопросы по вводу для предыдущих выпусков IDE.
Обработчик аннотаций, используемый в качестве примера, создает родительский класс для аннотированного класса. Созданный родительский класс также содержит метод, вызываемый из аннотированного класса. Следуйте указаниям по созданию и добавлению обработчика особых аннотаций в проект среды IDE, приведенным ниже.
Для работы с этим учебным курсом требуются программное обеспечение и ресурсы, перечисленные ниже.
Программное обеспечение или материал | Требуемая версия |
---|---|
7.0, 7.1, 7.2, 7.3 |
|
версия 6 или 7 |
|
версия 1.12.4 и более поздние |
Определение аннотации и создание обработчика аннотаций
В этом упражнении мы создадим проект библиотеки классов.
-
Выберите File ("Файл") > New Project ("Создать проект") и выберите тип проекта Java Class Library ("Библиотека классов Java") в категории Java. Нажмите кнопку "Далее".
-
Введите *
AnnProcessor
* в поле "Имя проекта" и укажите местоположение для проекта. Нажмите кнопку "Завершить".
При нажатии кнопки "Готово" среда IDE создаст проект библиотеки классов, который появится в окне Projects ("Проекты").
-
Щелкните правой кнопкой мыши узел проекта AnnProcessor в окне Projects ("Проекты") и выберите Properties ("Свойства").
-
В категории "Источники" подтвердите, что для JDK 6 или JDK 7 указан формат исходного кода/двоичный формат.
-
Выберите вкладку "Библиотеки" и подтвердите, что платформой Java является JDK 1.6 или JDK 1.7. Нажмите кнопку "ОК", чтобы закрыть окно Project Properties ("Свойства проекта").
В этом упражнении мы создадим несколько пакетов Java и по одному классу Java в каждом из пакетов.
-
В проекте 'AnnProcessor' щелкните правой кнопкой мыши узел 'Пакеты исходных кодов' и выберите 'Создать > Пакет Java'.
-
Введите *
ann
* в поле "Имя пакета" и нажмите кнопку "Готово" для создания нового пакета Java. -
Повторите два предыдущих действия, чтобы создать пакет Java под названием *
proc
*.
После создания двух пакетов Java структура проекта должна быть подобной изображенной ниже.

-
Щелкните правой кнопкой мыши пакет Java
ann
и выберите New ("Создать") > Java class ("Класс Java"). -
Введите *
Handleable
* в поле Class Name ("Имя класса"). Нажмите кнопку "Завершить". -
Измените файл
Handleable.java
, добавив приведенный ниже код. Сохраните файл.
package ann;
public *@interface* Handleable {
}
Так объявляются аннотации — совершенно аналогично объявлению интерфейса. Различием является то, что ключевому слову interface
должен предшествовать знак @
. Эта аннотация именуется Handleable
(обрабатываемой).
Дополнительные сведения. В объявлениях аннотаций можно указать дополнительные параметры, например, типы элементов, к которым можно добавить аннотации, например, классы или методы. Для этого добавляется @Target(value = {ElementType.TYPE})
для классов и @Target(value = {ElementType.METHOD}).
Таким образом, объявление аннотаций становится автоматическим с помощью мета-аннотаций.
Теперь необходимо добавить к обработчику аннотаций код для обработки аннотации Handleable
.
-
Щелкните правой кнопкой мыши пакет*
Java
proc* и выберите New ("Создать") > Java class ("Класс Java"). -
Введите *
HandleableProcessor
* в поле Class Name ("Имя класса"). Нажмите кнопку "Завершить". -
Измените класс
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);
}
*}
-
Создайте проект, щелкнув правой кнопкой мыши проект
AnnProcessor
и выбрав 'Сборка'.
Работа с обработчиком аннотаций в среде IDE
В этом разделе мы создадим приложение Java, в котором будет использоваться обработчик аннотаций.
-
Выберите File ("Файл") > New Project ("Создать проект") и выберите тип проекта Java Application ("Приложение Java") в категории Java. Нажмите кнопку "Далее".
-
На странице "Имя и расположение" введите *
Demo
* в поле "Имя проекта" и укажите расположение проекта. -
Введите *
demo.Main
* в поле Create Main Class ("Создать главный класс"). Нажмите кнопку "Завершить".

-
Откройте окно 'Свойства проекта' и убедитесь, что JDK 6 или JDK 7 выбран как двоичный формат/формат исходного кода на панели "Исходные коды", а также что JDK 1.6 or JDK 1.7 установлен как платформа Java на панели "Библиотеки".
-
Измените класс
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 сообщает о ряде ошибок компиляции. Это происходит, поскольку обработчик аннотаций еще не добавлен в проект.
-
Щелкните правой кнопкой мыши узел проекта
Demo
в окне "Проекты", выберите "Свойства", затем выберите категорию "Библиотеки" в окне 'Свойства проекта'. -
На вкладке Compile ("Компиляция") щелкните Add Project ("Добавить проект") и найдите проект
AnnProcessor
.

Вкладка "Компиляция" соответствует параметру -classpath
компилятора Java. Поскольку обработчик аннотаций является единым файлом JAR, который содержит как определение аннотаций, так и обработчик аннотаций, его следует добавить к пути классов для проекта, которым является вкладка Compile ("Компиляция").
-
Выберите категорию "Компиляция" в окне "Свойства проекта" и установите флажки "Разрешить обработку аннотаций" и "Разрешить обработку аннотаций в редакторе".
-
Укажите, какой обработчик аннотаций должен быть запущен, нажав кнопку Add ("Добавить") рядом с текстовой областью обработчиков аннотаций и введя *
proc.HandleableProcessor
* в поле FQN ("Полностью определенное имя") обработчика аннотаций.

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

-
Нажмите кнопку OK в окне Properties ("Свойства").
Примечание. В файле Main.java
все еще могут отображаться ошибки компиляции. Это происходит, поскольку в среде IDE еще не определено местоположение файла MainExtras.java
, в котором объявляется метод handleStuff
. После первого создания проекта Demo будет создан файл MainExtras.java
. Если для проекта включено режим Compile On Save ("Компилировать при сохранении"), среда IDE компилирует проект при сохранении Main.java
.
-
Щелкните правой кнопкой мыши проект Demo и выберите Build ("Сборка").
Если после сборки проекта взглянуть на него в окне Projects ("Проекты"), то можно будет увидеть новый узел Generated Sources
с файлом demo/MainExtras.java
.

При просмотре содержимого созданного файла 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);
}
}
-
Щелкните правой кнопкой мыши проект Demo и выберите Run ("Запустить").
При щелчке Run ("Запустить") в окне вывода можно будет увидеть следующее. Выполняется компиляция проекта Demo, и на экран выводится сообщение.

Дополнительные сведения
Ознакомьтесь со следующими ресурсами для получения дополнительных сведений об аннотациях в приложениях Java:
-
Документация Java SE - Аннотации
-
Учебный курс Java SE - Аннотации
-
Блог Джозефа Д. Дарси (Joseph D. Darcy) - полезные рекомендации от ведущего специалиста по спецификации JSR-269