Lombok @FieldNameConstants工作原理、实现机制及自定义方案问询
Lombok @FieldNameConstants 实现原理与自定义扩展方案
@FieldNameConstants注解会为目标类生成一个内部类Fields,这个内部类包含对应类中每个字段的名称常量(被@FieldNameConstants.Exclude标记的字段会被排除)。下面两段代码编译后的效果完全等价:
import lombok.experimental.FieldNameConstants; @FieldNameConstants public class FieldNameConstantsExample { private final String iAmAField; private final int andSoAmI; @FieldNameConstants.Exclude private final int asAmI; }
public class FieldNameConstantsExample2 { private final String iAmAField; private final int andSoAmI; private final int asAmI; public static final class Fields { public static final String iAmAField = "iAmAField"; public static final String andSoAmI = "andSoAmI"; } }
一、Lombok 是如何实现这个功能的?
Lombok的核心依赖编译期注解处理器,它在Java编译的注解处理阶段介入工作,具体流程如下:
- 编译器启动后,加载Lombok注册的
FieldNameConstantsProcessor注解处理器 - 处理器扫描所有带有
@FieldNameConstants注解的类,通过Java注解API读取类的元数据(包括字段列表、字段上的注解标记等) - 过滤掉被
@FieldNameConstants.Exclude标记的字段,为剩余字段生成对应的常量定义 - 通过字符串拼接或JavaPoet工具生成
Fields内部类的源代码 - 最后通过编译器API,将生成的代码注入到目标类的编译字节码中,相当于编译时自动帮你完成了
Fields类的编写
整个过程在编译阶段完成,运行时没有额外性能损耗,和手动编写的代码完全一致。
二、如何自定义字段注解来定制Fields中的常量?
如果想通过自定义字段注解指定常量值(类似@FieldNameConstants(value="XXX")的效果),可以自己实现一套注解处理器,步骤如下:
1. 自定义字段级注解
先定义一个用于指定常量名称的字段注解:
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.FIELD) @Retention(RetentionPolicy.SOURCE) // 仅在源码阶段生效,编译后自动丢弃 public @interface CustomFieldName { String value(); }
同时定义一个类注解,标记需要生成Fields内部类的类:
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 GenerateFields {}
2. 编写注解处理器
继承AbstractProcessor实现处理逻辑,这里用JavaPoet简化代码生成(也可以手动拼接字符串):
import com.squareup.javapoet.*; import javax.annotation.processing.*; import javax.lang.model.SourceVersion; import javax.lang.model.element.*; import javax.tools.Diagnostic; import java.util.Set; @SupportedAnnotationTypes("com.yourpackage.GenerateFields") // 替换为你的类注解全类名 @SupportedSourceVersion(SourceVersion.RELEASE_8) public class CustomFieldNameProcessor extends AbstractProcessor { @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // 扫描所有标记了@GenerateFields的类 for (Element element : roundEnv.getElementsAnnotatedWith(GenerateFields.class)) { if (element.getKind() != ElementKind.CLASS) { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@GenerateFields只能标记类"); continue; } TypeElement typeElement = (TypeElement) element; String className = typeElement.getSimpleName().toString(); String packageName = processingEnv.getElementUtils().getPackageOf(typeElement).getQualifiedName().toString(); // 构建Fields内部类 TypeSpec.Builder fieldsClassBuilder = TypeSpec.classBuilder("Fields") .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL); // 遍历类的所有字段 for (Element enclosedElement : typeElement.getEnclosedElements()) { if (enclosedElement.getKind() != ElementKind.FIELD) continue; VariableElement fieldElement = (VariableElement) enclosedElement; String fieldName = fieldElement.getSimpleName().toString(); // 读取字段上的@CustomFieldName注解,获取自定义常量值 CustomFieldName customName = fieldElement.getAnnotation(CustomFieldName.class); String constantValue = customName != null ? customName.value() : fieldName; // 添加常量字段到Fields类 fieldsClassBuilder.addField(FieldSpec.builder(String.class, fieldName) .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) .initializer("$S", constantValue) .build()); } // 生成并写入类文件 JavaFile javaFile = JavaFile.builder(packageName, fieldsClassBuilder.build()).build(); try { javaFile.writeTo(processingEnv.getFiler()); } catch (Exception e) { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "生成Fields类失败:" + e.getMessage()); } } return true; } }
3. 注册注解处理器
在项目的src/main/resources/META-INF/services目录下创建javax.annotation.processing.Processor文件,内容为你的处理器全类名:
com.yourpackage.CustomFieldNameProcessor
4. 使用示例
@GenerateFields public class Demo { @CustomFieldName("custom_user_name") private String userName; private int age; }
编译后会自动生成内部类:
public static final class Fields { public static final String userName = "custom_user_name"; public static final String age = "age"; }
内容的提问来源于stack exchange,提问作者Calimero
相关产品推荐
相关产品推荐

