前沿
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注解如何使用
比如我们自定义了一个注解,这个注解在项目中只能被使用一次,如果被使用到多次的时候就会让编译失败。
- 注解定义
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
/**
* @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的注解不仅仅在运行时利用反射提供很多的功能,而且在编译时也能为我们实现很多的功能,比如代码规则校验、代码自动生成等。