轩辕李的博客 轩辕李的博客
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

轩辕李

勇猛精进,星辰大海
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Java

    • 核心

      • Java8--Lambda 表达式、Stream 和时间 API
      • Java集合
      • Java IO
      • Java 文件操作
      • Java 网络编程
      • Java运行期动态能力
      • Java可插入注解处理器
        • 什么是Java可插入注解处理器
        • 注解处理器的应用场景
        • 如何实现自定义注解处理器
          • 1. 创建自定义注解
          • 2. 创建注解处理器
          • 3. 注册注解处理器
          • 4. 应用自定义注解
        • Maven配置
        • IntelliJ IDEA配置
        • 注意事项
        • 结论
      • Java基准测试(JMH)
      • Java性能分析(Profiler)
      • Java调试(JDI与JDWP)
      • Java管理与监控(JMX)
      • Java加密体系(JCA)
      • Java服务发现(SPI)
      • Java随机数生成研究
      • Java数据库连接(JDBC)
      • Java历代版本新特性
      • 写好Java Doc
      • 聊聊classpath及其资源获取
    • 并发

    • 经验

    • JVM

    • 企业应用

  • Spring

  • 其他语言

  • 工具

  • 后端
  • Java
  • 核心
轩辕李
2023-02-05
目录

Java可插入注解处理器

先前文章中讲到了Java运行期动态能力,其中提到了Java编译器动态能力:JSR 269,今天来认识一下它。

Java可插入注解处理器(Pluggable Annotation Processing API)是一种强大的编译时代码处理工具,允许开发者在编译时处理和解析Java源代码中的注解。

本文将详细介绍Java可插入注解处理器的概念、工作原理和如何实现自定义注解处理器。我们将创建一个简单的示例,通过自定义注解生成一个Builder类。

# 什么是Java可插入注解处理器

Java可插入注解处理器,即JSR 269(Java Specification Request 269),提供了一套API,让开发者能够在编译时处理和解析Java源代码中的注解。注解处理器在编译阶段运行,可以用于静态代码分析、生成额外的代码或者验证代码约束。这种机制使得开发者可以在编译时执行自定义操作,从而提高代码的质量和可维护性。

# 注解处理器的应用场景

注解处理器通常应用于以下场景:

  1. 自动生成代码:例如,生成实现接口的类、创建特定的类或方法等。
  2. 静态代码分析:例如,检查代码风格、寻找潜在的性能问题或者检测代码中的错误。
  3. 验证代码约束:例如,检查方法参数、验证类之间的关系等。

# 如何实现自定义注解处理器

要实现自定义注解处理器,您需要完成以下步骤:

# 1. 创建自定义注解

首先,创建一个名为Builder.java的文件,定义一个名为Builder的注解:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface Builder {
}

@Target注解表示这个注解只能应用于类或接口,@Retention注解表示这个注解在源代码级别保留,不会被编译到字节码文件中。

# 2. 创建注解处理器

接下来,创建一个名为BuilderProcessor.java的文件,实现一个名为BuilderProcessor的注解处理器类:

import java.io.IOException;
import java.io.Writer;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
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.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.MirroredTypeException;
import javax.lang.model.type.TypeMirror;
import javax.tools.JavaFileObject;

@SupportedAnnotationTypes("Builder")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class BuilderProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        for (TypeElement annotation : annotations) {
            Set<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(annotation);
            for (Element element : annotatedElements) {
                if (element.getKind() != ElementKind.CLASS) {
                    // 只支持应用于类上
                    continue;
                }
                TypeElement typeElement = (TypeElement) element;
                String packageName = getPackageName(typeElement);
                String className = typeElement.getSimpleName().toString();
                String builderClassName = className + "Builder";
                Filer filer = processingEnv.getFiler();
                try {
                    JavaFileObject builderFile = filer.createSourceFile(packageName + "." + builderClassName);
                    Writer writer = builderFile.openWriter();
                    writer.write("package " + packageName + ";\n\n");
                    writer.write("public class " + builderClassName + " {\n");
                    writer.write("\tprivate final " + className + " instance = new " + className + "();\n\n");
                    for (Element field : typeElement.getEnclosedElements()) {
                        if (field.getKind() != ElementKind.FIELD || field.getModifiers().contains(Modifier.STATIC)) {
                            // 只支持实例变量
                            continue;
                        }
                        String fieldName = field.getSimpleName().toString();
                        String fieldType = field.asType().toString();
                        writer.write("\tpublic " + builderClassName + " " + fieldName + "(" + fieldType + " " + fieldName + ") {\n");
                        writer.write("\t\tinstance." + fieldName + " = " + fieldName + ";\n");
                        writer.write("\t\treturn this;\n");
                        writer.write("\t}\n\n");
                    }
                    writer.write("\tpublic " + className + " build() {\n");
                    writer.write("\t\treturn instance;\n");
                    writer.write("\t}\n");
                    writer.write("}\n");
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return true;
    }

    private String getPackageName(TypeElement element) {
        return processingEnv.getElementUtils().getPackageOf(element).getQualifiedName().toString();
    }
}

@SupportedAnnotationTypes注解表示这个处理器支持Builder注解,@SupportedSourceVersion注解表示这个处理器支持Java 8源代码版本。

在process方法中,您可以实现注解处理逻辑,例如生成Builder类。

# 3. 注册注解处理器

要注册注解处理器,您需要在项目的resources/META-INF/services目录下,创建名为javax.annotation.processing.Processor的文件,该文件包含您实现的注解处理器类的完整类名。

# 4. 应用自定义注解

现在,您可以在您的Java项目中使用@Builder注解。创建一个名为Person.java的文件,定义一个名为Person的类,并应用@Builder注解:

@Builder
public class Person {
    private String firstName;
    private String lastName;
    private int age;

    // 省略 getter 和 setter 方法
}

当您编译这个类时,注解处理器将处理@Builder注解,并可以生成相应的Builder类。

# Maven配置

在Maven项目的pom.xml文件中,添加maven-compiler-plugin插件,并配置annotationProcessorPaths以引入自定义注解处理器。这是一个示例配置:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.0</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <annotationProcessorPaths>
                    <path>
                        <groupId>com.example</groupId>
                        <artifactId>annotation-processor</artifactId>
                        <version>1.0.0</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

请将<groupId>, <artifactId>和<version>替换为您自定义注解处理器的实际值。

# IntelliJ IDEA配置

要在IntelliJ IDEA中启用注解处理器,请按照以下步骤操作:

  1. 打开IntelliJ IDEA,选择“File” > “Settings”(对于macOS用户,“IntelliJ IDEA” > “Preferences”)。
  2. 在设置窗口中,展开“Build, Execution, Deployment”选项,然后点击“Compiler” > “Annotation Processors”。
  3. 勾选“Enable annotation processing”选项。
  4. 在“Processor path”区域,选择“Use module compile output path”选项。
  5. 在“Processor FQCN”区域,添加您的自定义注解处理器的完整类名(例如com.example.BuilderProcessor)。
  6. 点击“OK”保存设置。

完成上述配置后,IntelliJ IDEA将在编译项目时自动运行您的自定义注解处理器。如果需要,您还可以根据项目需求调整其他注解处理器相关的设置。

请注意,如果您的项目是一个Maven项目,并且已经在pom.xml文件中正确配置了自定义注解处理器,IntelliJ IDEA通常会自动识别并启用注解处理器。在这种情况下,您可能不需要手动进行上述配置。

# 注意事项

请注意,JSR 269 API仅支持在编译时处理注解。如果您需要在运行时处理注解,可以使用Java的反射API。

此外,随着Kotlin的成熟,可以考虑使用KSP(Kotlin Symbol Processing),它是JetBrains推出的一款Kotlin编译器插件,用于实现高效的符号处理。
KSP提供了一套简洁的API,可以替代Java的注解处理器(Annotation Processor),实现更高效的编译时代码生成和检查。
关于Kotlin的使用,参考:从Java到Kotlin

# 结论

Java可插入注解处理器为Java编程语言提供了一种在编译时处理注解的方法。

通过自定义注解处理器,您可以在编译时生成代码、分析代码或验证代码约束,从而提高代码的质量和可维护性。

本文通过一个简单的示例介绍了如何实现自定义注解处理器,您可以根据实际需求为您的项目定制合适的注解处理器。

祝你变得更强!

编辑 (opens new window)
#JSR 269#可插入注解处理器
上次更新: 2023/06/14
Java运行期动态能力
Java基准测试(JMH)

← Java运行期动态能力 Java基准测试(JMH)→

最近更新
01
Spring Boot版本新特性
09-15
02
Spring框架版本新特性
09-01
03
Spring Boot开发初体验
08-15
更多文章>
Theme by Vdoing | Copyright © 2018-2025 京ICP备2021021832号-2 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式