插入式注解的浅浅探究
· 阅读需 7 分钟
介绍
类似lombok,通过@Data注解,即可将getter/setter/toString等方法注入到编译后的class代码中。
本文即是对它的原理的探究。
编写代码
一、定义@MyData注解
先看一下 lombok 如何定义
@Data注解的lombok.Data
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface Data {
String staticConstructor() default "";
}
仿照@Data写一个@MyData注解:
com.one.annotation.MyData
@Target(ElementType.TYPE) // 标注在类上
@Retention(RetentionPolicy.SOURCE) // 仅在源码级别保留
public @interface MyData {
}
二、实现处理器Processor
插入式注解处理器,该处理器是实现了JSR-269提案定义的Pluggable Annotation Processing API实现
模拟功能:生成一个类,类里面有个sayHello方法。(简单了解)
后面实现了一个高级点的处理器。点此跳转🚩
添加类MyDataProcessor
com.one.process.MyDataProcessor
package com.one.process;
import com.one.annotation.MyData;
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.TypeElement;
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Set;
@SupportedAnnotationTypes("com.one.annotation.MyData") // 处理的注解类型
@SupportedSourceVersion(SourceVersion.RELEASE_8) // 支持的 Java 版本
public class MyDataProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (Element element : roundEnv.getElementsAnnotatedWith(MyData.class)) {
if (element instanceof TypeElement) {
TypeElement typeElement = (TypeElement) element;
String className = typeElement.getSimpleName().toString();
String packageName = processingEnv.getElementUtils().getPackageOf(typeElement).toString();
// 生成代码
generateCode(className, packageName);
}
}
return true;
}
private void generateCode(String className, String packageName) {
try {
// 创建新的 Java 文件
JavaFileObject file = processingEnv.getFiler().createSourceFile(packageName + "." + className + "Generated");
try (PrintWriter writer = new PrintWriter(file.openWriter())) {
// 写入生成的代码
writer.println("package " + packageName + ";");
writer.println();
writer.println("public class " + className + "Generated {");
writer.println(" public String sayHello() {");
writer.println(" return \"Hello\";");
writer.println(" }");
writer.println("}");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
三、注册注解处理器(SPI)
注解处理器需要SPI服务发现机制
在src/main/resources/META-INF/services/javax.annotation.processing.Processor内编写:
com.one.process.MyDataProcessor
四、使用注解
com.one.domain.User
@MyData
public class User {
private String name;
private int age;
}
五、编译并查看生成代码
可以使用CTRL+F9,构建代码,在target/classes中找到User.class同目录中有一个UserGenerated.class
target/
