You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 23:57:28