如何实现类似Lombok @Getter的自定义@MyGetter编译期注解?
实现自定义@MyGetter注解生成带条件判断的Getter
一、定义@MyGetter注解
首先创建注解类,指定其仅在编译阶段生效,作用目标为类:
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 MyGetter { }
二、编写注解处理器(核心逻辑)
创建继承AbstractProcessor的处理器类,负责扫描带@MyGetter的类,为私有String字段生成带条件判断的Getter方法:
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.lang.model.element.VariableElement; import javax.lang.model.type.TypeKind; import javax.tools.JavaFileObject; import java.io.IOException; import java.io.Writer; import java.util.Set; @SupportedAnnotationTypes("com.yourpackage.MyGetter") // 替换为你的注解全类名 @SupportedSourceVersion(SourceVersion.RELEASE_8) // 根据JDK版本调整 public class MyGetterProcessor extends AbstractProcessor { @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // 遍历所有标注@MyGetter的类 for (Element element : roundEnv.getElementsAnnotatedWith(MyGetter.class)) { if (!element.getKind().isClass()) continue; TypeElement classElement = (TypeElement) element; String className = classElement.getSimpleName().toString(); String packageName = processingEnv.getElementUtils().getPackageOf(classElement).toString(); try { // 创建目标类的Java文件对象 JavaFileObject file = processingEnv.getFiler().createSourceFile(packageName + "." + className); try (Writer writer = file.openWriter()) { // 写入包声明与类注解 writer.write("package " + packageName + ";\n\n"); writer.write("@MyGetter\n"); writer.write("public class " + className + " {\n\n"); // 遍历类内所有元素,原样保留非目标字段/方法,生成目标Getter for (Element enclosedElement : classElement.getEnclosedElements()) { if (enclosedElement.getKind().isField()) { VariableElement field = (VariableElement) enclosedElement; // 仅处理私有String类型字段 if (field.getModifiers().contains(javax.lang.model.element.Modifier.PRIVATE) && field.asType().toString().equals("java.lang.String")) { String fieldName = field.getSimpleName().toString(); // 生成Getter方法名(小驼峰转大驼峰) String getterName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); // 写入原字段声明 writer.write(" private String " + fieldName + ";\n\n"); // 写入带条件判断的Getter方法 writer.write(" public String " + getterName + "() {\n"); writer.write(" if (isHexString()) {\n"); writer.write(" // 替换为你的实际处理逻辑\n"); writer.write(" return \"processed_\" + " + fieldName + ";\n"); writer.write(" }\n"); writer.write(" return " + fieldName + ";\n"); writer.write(" }\n\n"); } else { // 非目标字段原样写入 writer.write(" " + enclosedElement + ";\n\n"); } } else if (enclosedElement.getKind().isMethod()) { // 原有方法(如isHexString)原样保留 writer.write(" " + enclosedElement + "\n\n"); } } writer.write("}\n"); } } catch (IOException e) { processingEnv.getMessager().printError("生成Getter失败: " + e.getMessage()); } } return true; } }
三、注册注解处理器
让编译器识别这个处理器,有两种常用方式:
方式1:手动配置META-INF
在项目src/main/resources/META-INF/services目录下创建文件javax.annotation.processing.Processor,内容为处理器全类名:
com.yourpackage.MyGetterProcessor
方式2:使用AutoService自动注册(推荐)
添加Maven依赖:
<dependency> <groupId>com.google.auto.service</groupId> <artifactId>auto-service</artifactId> <version>1.0.1</version> <scope>provided</scope> </dependency>
然后在处理器类上添加注解:
import com.google.auto.service.AutoService; import javax.annotation.processing.Processor; @AutoService(Processor.class) @SupportedAnnotationTypes("com.yourpackage.MyGetter") @SupportedSourceVersion(SourceVersion.RELEASE_8) public class MyGetterProcessor extends AbstractProcessor { // 原有代码不变 }
四、测试使用
创建测试类(需包含isHexString判断方法):
@MyGetter public class AnyClass { private String fieldVariable; // 自定义的十六进制判断方法 private boolean isHexString() { return fieldVariable != null && fieldVariable.matches("[0-9A-Fa-f]+"); } }
编译后会自动生成包含目标Getter的类文件,效果如下:
package com.yourpackage; @MyGetter public class AnyClass { private String fieldVariable; private boolean isHexString() { return fieldVariable != null && fieldVariable.matches("[0-9A-Fa-f]+"); } public String getFieldVariable() { if (isHexString()) { // 替换为你的实际处理逻辑 return "processed_" + fieldVariable; } return fieldVariable; } }
注意事项
- 确保处理器能正确保留类中原有的非目标元素(如其他字段、方法)。
isHexString方法需在目标类中存在,若需要自动生成该方法,可扩展处理器逻辑添加对应代码。- 字段名转驼峰的逻辑可根据需求优化(比如处理字段名以多个大写字母开头的场景)。
内容的提问来源于stack exchange,提问作者TomYOON
相关产品推荐
相关产品推荐

