NetBeans IDE 中的标注处理程序支持,第二部分:在 IDE 中使用自己的定制标注处理程序

编写人:Jesse Glick,编写和维护人:Irina Filippova

此页上的内容适用于 NetBeans IDE 7.0、7.1、7.2 和 7.3

在本教程的此部分,您会了解到如何在 IDE 中将自创的定制标注处理程序添加到项目中。本教程的目的并不在于教授如何编写标注处理程序。它解释如何将其添加到 NetBeans IDE 项目中。

此部分使用的样例应用程序由 Jesse Glick 创建,并作为早期发行版本的 IDE 的常见问题解答条目发布。

用作示例的标注处理程序为标注类生成父类。生成的父类还包含从标注类中调用的方法。请按照下面有关如何创建定制标注处理程序并将其添加到 IDE 项目的说明进行操作。

要学完本教程,您需要具备以下软件和资源。

软件或资源 要求的版本
NetBeans IDE 7.0, 7.1, 7.2, 7.3
Java 开发工具包 (JDK) 版本 6 或 7
lombok.jar v1.12.4 或更新版本

定义标注与创建标注处理程序

在本练习中,将创建类库项目。

  1. 选择 "File"(文件)> "New Project"(新建项目),然后在 "Java" 类别中选择 "Java Class Library"(Java 类库)项目类型。单击 "Next"(下一步)。
  2. 键入 AnnProcessor 作为项目名称,并为项目指定位置。单击 "Finish"(完成)。

    单击 "Finish"(完成),此时 IDE 将创建类库项目,并在 "Projects"(项目)窗口中列出该项目。

  3. 在 "Projects"(项目)窗口中,右键单击 AnnProcessor 项目节点,然后选择 "Properties"(属性)。
  4. 在 "Sources"(源)类别中,确认将 JDK 6 或 JDK 7 指定为源代码/二进制格式。
  5. 选择 "Libraries"(库)标签,然后确认将 Java 平台设置为 "JDK 1.6" 或 "JDK 1.7"。单击 "OK"(确定),以关闭 "Project Properties"(项目属性)窗口。

在本练习中,将创建两个 Java 包,并在每个包中创建一个 Java 类。

  1. 右键单击 "AnnProcessor" 项目下的 "Source Packages"(源包)节点,然后选择 "New"(新建)> "Java Package"(Java 包)。
  2. 为 "Package Name"(包名)键入 ann,然后单击 "Finish"(完成)创建新的 Java 包。
  3. 重复执行前面的两个步骤,以创建一个名为 proc 的 Java 包。

    创建了这两个 Java 包后,项目结构应类似于下图。

    显示 Java 包的 "Projects"(项目)窗口的屏幕快照
  4. 右键单击 ann Java 包,然后选择 "New"(新建)> "Java Class"(Java 类)。
  5. 键入 Handleable 作为类名。单击 "Finish"(完成)。
  6. 修改新的 Handleable.java 文件,进行如下更改。保存该文件。
    package ann;
    
    public @interface Handleable {
    
    }

    这是一种标注声明方式,与接口声明十分类似。区别在于 at 符号 (@) 必须置于 interface 关键字之前。此标注名为 Handleable

    其他信息。在标注声明中,还可以指定其他参数,例如,可以标注哪些类型的元素(类或方法等)。可以通过为类添加 @Target(value = {ElementType.TYPE}) 和添加 @Target(value = {ElementType.METHOD}) 来实现此目的。因此,标注声明使用 meta-annotations 标注自身。

    现在,您需要为标注处理程序添加处理 Handleable 标注的代码。

  7. 右键单击 proc Java 包,然后选择 "New"(新建)> "Java Class"(Java 类)。
  8. 键入 HandleableProcessor 作为类名。单击 "Finish"(完成)。
  9. 修改 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)

    然后,为处理程序声明一个公共类,以扩展 javax.annotation.processing 包中的 AbstractProcessor 类。AbstractProcessor 是具体标注处理程序的标准超类,它包含处理标注所需的方法。

    public class HandleableProcessor extends AbstractProcessor {
    ...
    }

    现在,您需要为该类提供一个公共构造函数。

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

    然后,调用父 AbstractProcessor 类的 process() 方法。通过此方法,提供可用于处理的标注。此外,此方法包含有关处理舍入的信息。

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

    标注处理程序的逻辑包含在 AbstractProcessor 类的 process() 方法中。注:通过 AbstractProcessor,还可以访问 ProcessingEnvironment 接口,该接口允许标注处理程序使用多个有用的工具,如 Filer(使标注处理程序可以创建新文件的 Filer 处理程序)和 Messager(标注处理程序报告错误的一种方式)。

    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);
    }
    }
  10. 右键单击 AnnProcessor 项目,然后选择 "Build"(构建)以构建项目。

在 IDE 中使用标注处理程序

在本部分中,将创建一个 Java 应用程序项目,以便在其中使用标注处理程序。

  1. 选择 "File"(文件)> "New Project"(新建项目),然后在 "Java" 类别中选择 "Java Application"(Java 应用程序)项目类型。单击 "Next"(下一步)。
  2. 在 "Name and Location"(名称和位置)页中,键入 Demo 作为项目名称,并指定项目位置。
  3. 在 "Create Main Class"(创建主类)字段中,键入 demo.Main。单击 "Finish"(完成)。
    新建项目向导的屏幕快照
  4. 打开 "Project Properties"(项目属性)窗口,确认在 "Sources"(源)面板中选择 "JDK 6" 或 "JDK 7" 作为源代码/二进制格式,然后确认在 "Libraries"(库)面板中将 Java 平台设置为 "JDK 1.6" 或 "JDK 1.7"。
  5. 修改 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 的 import 语句
    • 扩展 MainExtras 类(MainExtras 应由标注处理程序在编译期间生成)的公共类 Main
    • 一个名为 stuff 的私有字段,它使用 @Handleable 标注进行标注
    • 调用 handleStuff 方法的 main 方法,后者在自动生成的 MainExtras 类中声明

      在这个简单示例中,handleStuff 方法仅输出当前值。可以修改此方法以执行其他任务。

    保存 Main.java 代码后,您会看到 IDE 报告多个编译错误。这是由于标注处理程序尚未添加到项目中。

  6. 在 "Projects"(项目)窗口中,右键单击 Demo 项目节点,选择 "Properties"(属性),然后在 "Project Properties"(项目属性)窗口中选择 "Libraries"(库)类别。
  7. 在 "Compile"(编译)标签中,单击 "Add Project"(添加项目),然后找到 AnnProcessor 项目。
    项目的 "Properties"(属性)窗口 "Libraries"(库)类别中的 "Compile"(编译)标签的屏幕快照

    "Compile"(编译)标签对应于 Java 编译器-classpath 选项。由于标注处理程序是包含标注定义和标注处理程序的单一 JAR 文件,因此,应在 "Compile"(编译)标签中将其添加到项目的类路径中。

  8. 在 "Project Properties"(项目属性)窗口中选择 "Compiling"(编译)类别,然后选中 "Enable Annotation Processing"(启用标注处理)和 "Enable Annotation Processing in Editor"(在编辑器中启用标注处理)复选框。
  9. 单击 "Annotation Processors"(标注处理程序)文本区域旁边的 "Add"(添加)按钮,然后在 "Annotation Processor FQN"(标注处理程序 FQN)字段中键入 proc.HandleableProcessor 以指定要运行的标注处理程序。
    "Annotation Processor FQN"(标注处理程序 FQN)对话框的屏幕快照

    "Project Properties"(项目属性)窗口中的 "Compiling"(编译)类别应如下图所示。

    项目 "Properties"(属性)窗口中的 "Compiling"(编译)类别的屏幕快照
  10. 在 "Properties"(属性)窗口中单击 "OK"(确定)。

    注:Main.java 文件中,仍可能会看到一些编译错误。这是由于 IDE 仍然找不到声明 handleStuff 方法的 MainExtras.java 文件。首次构建 Demo 项目后,将构建 MainExtras.java 文件。如果为项目启用了 "Compile On Save"(在保存时编译)功能,则在保存 Main.java 时,IDE 将编译项目。

  11. 右键单击 "Demo" 项目,然后选择 "Build"(构建)。

    在构建项目后,如果在 "Projects"(项目)窗口中查看该项目,则可以看到包含 demo/MainExtras.java 文件的新 Generated Sources(构建的源文件)节点。

    带有 "Generated Sources"(生成的源文件)的 "Projects"(项目)窗口的屏幕快照

    如果您查看生成的 MainExtras.java 文件内容,则可以看到标注处理程序生成了包含 handleStuff 方法的 MainExtras 类。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);
        }
    }
  12. 右键单击 "Demo" 项目,然后选择 "Run"(运行)。

    单击 "Run"(运行)时,"Output"(输出)窗口中应显示以下内容。Demo 项目编译并打印该信息。

    带有 "Generated Sources"(生成的源文件)的 "Projects"(项目)窗口的屏幕快照

另请参见

有关 Java 应用程序中标注的详细信息,请参见以下资源:

get support for the NetBeans

Support


By use of this website, you agree to the NetBeans Policies and Terms of Use. © 2013, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo