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

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

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

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

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

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

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

软件或资源 要求的版本
NetBeans IDE 7.1, 7.2, 7.3
Java 开发工具包 (JDK) 版本 6

注:

  • NetBeans IDE 6.9 发行版中增加了对定制标注处理程序的支持。本教程不适用于早期版本的 IDE。

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

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

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

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

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

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

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

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

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

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

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

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

  6. 右键单击 proc Java 包,然后选择 "New"(新建)> "Java Class"(Java 类)。
  7. 键入 HandleableProcessor 作为类名。单击 "Finish"(完成)。
  8. 修改 HandleableProcessor.java 类,以添加如下代码。保存所做的更改。
    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_6)
    public class HandleableProcessor extends AbstractProcessor {
    
        /** public for ServiceLoader */
        public HandleableProcessor() {
        }
    
        public boolean process(Set 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 annotations,
                RoundEnvironment roundEnv) {
         ...
         }
    
    }

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

    public class HandleableProcessor extends AbstractProcessor {
       ...
         public boolean process(Set 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);
    }
    }
  9. 右键单击 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. 打开项目的 "Properties"(属性)窗口,确认在 "Sources"(源)面板中选择 "JDK 6" 作为源代码/二进制格式,然后确认在 "Libraries"(库)面板中将 Java 平台设置为 "JDK 6"。
  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"(属性),然后在 "Properties"(属性)窗口中选择 "Libraries"(库)类别。
  7. 在 "Compile"(编译)标签中,单击 "Add Project"(添加项目),然后找到 AnnProcessor 项目。
    项目的

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

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

    "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(构建的源文件)节点。

    带有

    如果您查看生成的 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 项目编译并打印该信息。

    带有

另请参见

有关 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