前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java实践指南:编译时自定义注解处理器

Java实践指南:编译时自定义注解处理器

作者头像
崔认知
发布2023-06-19 14:34:05
4860
发布2023-06-19 14:34:05
举报
文章被收录于专栏:nobody


前沿


java中的注解(Annotation),我们每天每时每刻都在看到它,尤其是spring项目中。

运行时的注解+反射的合理利用,让一些功能的实现变的很容易,比如spring中的注解org.springframework.beans.factory.annotation.Autowired、org.springframework.beans.factory.annotation.Value等实现了注入值的一些功能。

其实java中的注解也可以在编译期间被我们所利用,比如自动代码生成框架lombok,google开源https://github.com/google/auto等。

编译时java注解如何使用


比如我们自定义了一个注解,这个注解在项目中只能被使用一次,如果被使用到多次的时候就会让编译失败。

  • 注解定义
代码语言:javascript
复制
package com.renzhikeji.annotation.processor;

import java.lang.annotation.*;

/**
 * @author 认知科技技术团队
 * 微信公众号:认知科技技术团队
 */
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface RetryDatasource {
    String value() default "";
}

注意:我们的注解必须能在编译阶段能够保留,这由RetentionPolicy来定义的。

  • 编写自定义注解处理器Processor
代码语言:javascript
复制

/**
 * @author 认知科技技术团队
 * <p>
 * 微信公众号:认知科技技术团队
 */
public class RetryDataSourceCheckProcessor extends AbstractProcessor {

    public RetryDataSourceCheckProcessor() {
        super();
        System.out.println("RetryDataSourceCheckProcessor()============================");
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        System.out.println("getSupportedAnnotationTypes()============================" + RetryDatasource.class.getCanonicalName());
        return Set.of(RetryDatasource.class.getCanonicalName());
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        System.out.println("getSupportedSourceVersion()============================" + SourceVersion.latestSupported());
        return SourceVersion.latestSupported();
    }

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        System.out.println("init()============================");

    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        System.out.println("==============process ==============" + annotations);
        for (TypeElement annotation : annotations) {
            Set<? extends Element> elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(annotation);
            System.out.println("" + annotation + elementsAnnotatedWith);
            if (elementsAnnotatedWith.size() > 1) {
                throw new IllegalArgumentException("注解" + annotation + "用在了多个类型上" + elementsAnnotatedWith);
            }
        }
        return true;
    }
}

为了了解每个方法的运行,代码随便打印了些日志。

注意:

  • 自定义注解处理器必须包含一个public修饰的无参构造函数,ServiceLoader需要反射实例化。
  • 需要覆盖方法com.renzhikeji.annotation.processor.RetryDataSourceCheckProcessor#getSupportedAnnotationTypes,指定我们的注解处理器处理的注解类型。
  • 尽量覆盖方法com.renzhikeji.annotation.processor.RetryDataSourceCheckProcessor#getSupportedSourceVersion,指定我们的java源码版本,而不使用其默认实现。
  • 根据SPI,在目录META-INF/services下新建文件javax.annotation.processing.Processor

文件内容:

代码语言:javascript
复制
com.renzhikeji.annotation.processor.RetryDataSourceCheckProcessor

注意:此文件需要和上述两个步骤中的代码位于不同的jar包中,避免编译SPI找不到类失败。

运行示例

maven编译时会报错:

代码语言:javascript
复制
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile 
(default-compile) on project demo-api: Fatal error compiling: 
java.lang.IllegalArgumentException: 
注解corenzhikeji.annotation.processor.RetryDatasource用在了多个类型上
[com.renzhikeji.demo.api.demoapi.DemoApiApplication, 
com.renzhikeji.demo.api.demoapi.demo.Test] -> [Help 1]
[ERROR] 

源码地址:

https://gitee.com/whoamiy/demo/tree/java_Annotation_Processor/

总结


java的注解不仅仅在运行时利用反射提供很多的功能,而且在编译时也能为我们实现很多的功能,比如代码规则校验、代码自动生成等。


本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-04-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 认知科技技术团队 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档