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

如何通过注解等技术在编译时为Java类添加方法?类Lombok定制化实现

编译时基于自定义注解生成Java方法的替代方案

如果你想避开Lombok,自己实现编译时基于注解动态添加方法的逻辑,下面是几种可行的技术方案和代码示例:

一、Java原生注解处理器(Annotation Processing API)

这是最基础的原生方案,不需要任何第三方依赖,完全依托JDK自带的注解处理机制实现。

步骤1:定义自定义注解

首先创建一个仅在源码阶段生效的注解,用于标记需要生成getter的字段:

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 MyGetter {
}

步骤2:实现注解处理器

编写继承自AbstractProcessor的处理器类,扫描标注了@MyGetter的字段,生成对应的getter代码:

import javax.annotation.processing.*;
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.tools.JavaFileObject;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Set;

@SupportedAnnotationTypes("com.example.MyGetter")
@SupportedSourceVersion(SourceVersion.RELEASE_17)
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 instanceof VariableElement field)) continue;
            
            TypeElement enclosingClass = (TypeElement) field.getEnclosingElement();
            String className = enclosingClass.getSimpleName().toString();
            String packageName = processingEnv.getElementUtils().getPackageOf(enclosingClass).toString();
            String fieldName = field.getSimpleName().toString();
            String fieldType = field.asType().toString();

            // 生成包含getter的辅助类
            try {
                JavaFileObject file = processingEnv.getFiler().createSourceFile(packageName + "." + className + "Accessor");
                try (PrintWriter writer = new PrintWriter(file.openWriter())) {
                    writer.println("package " + packageName + ";");
                    writer.println();
                    writer.println("public class " + className + "Accessor {");
                    writer.println("    private final " + className + " target;");
                    writer.println();
                    writer.println("    public " + className + "Accessor(" + className + " target) {");
                    writer.println("        this.target = target;");
                    writer.println("    }");
                    writer.println();
                    // 生成标准getter方法
                    String getterName = "get" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
                    writer.println("    public " + fieldType + " " + getterName + "() {");
                    writer.println("        return target." + fieldName + ";");
                    writer.println("    }");
                    writer.println("}");
                }
            } catch (IOException e) {
                processingEnv.getMessager().printError("生成getter失败:" + e.getMessage());
            }
        }
        return true;
    }
}

步骤3:注册处理器

在项目的src/main/resources/META-INF/services目录下创建javax.annotation.processing.Processor文件,写入处理器的全类名:

com.example.MyGetterProcessor

测试

编写测试类并编译,处理器会自动生成UserAccessor.java:

package com.example;

public class User {
    @MyGetter
    private String username;
    private int age;

    public User(String username, int age) {
        this.username = username;
        this.age = age;
    }
}

二、Google Auto系列工具

Google Auto提供了简化注解处理器开发的工具,比如AutoService可以自动生成处理器注册文件,避免手动编写services文件。

添加依赖(Maven)

<dependency>
    <groupId>com.google.auto.service</groupId>
    <artifactId>auto-service</artifactId>
    <version>1.1.1</version>
    <scope>provided</scope>
</dependency>

修改处理器

只需给处理器类添加@AutoService(Processor.class)注解,就不需要手动创建services文件了:

import com.google.auto.service.AutoService;
import javax.annotation.processing.*;
// 其他导入省略

@AutoService(Processor.class)
@SupportedAnnotationTypes("com.example.MyGetter")
@SupportedSourceVersion(SourceVersion.RELEASE_17)
public class MyGetterProcessor extends AbstractProcessor {
    // 实现逻辑和原生方案一致
}

三、Byte Buddy编译时处理

Byte Buddy通常用于运行时代码生成,但也支持编译时通过注解处理器直接修改类的字节码,比原生API更简洁,适合需要直接修改原类的场景。

添加依赖(Maven)

<dependency>
    <groupId>net.bytebuddy</groupId>
    <artifactId>byte-buddy</artifactId>
    <version>1.14.11</version>
</dependency>
<dependency>
    <groupId>net.bytebuddy</groupId>
    <artifactId>byte-buddy-annotation-processing</artifactId>
    <version>1.14.11</version>
    <scope>provided</scope>
</dependency>

实现处理器

// 自定义注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface MyByteBuddyGetter {
}

// Byte Buddy处理器
import net.bytebuddy.build.Plugin;
import net.bytebuddy.build.Plugin.Factory.UsingReflection;
import net.bytebuddy.description.field.FieldDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FieldAccessor;
import net.bytebuddy.matcher.ElementMatchers;

@UsingReflection
public class MyByteBuddyGetterProcessor implements Plugin {

    @Override
    public DynamicType.Builder<?> apply(DynamicType.Builder<?> builder, TypeDescription typeDescription, Plugin.Context context) {
        // 给所有标注了@MyByteBuddyGetter的字段添加getter方法
        for (FieldDescription field : typeDescription.getDeclaredFields().filter(ElementMatchers.isAnnotatedWith(MyByteBuddyGetter.class))) {
            String getterName = "get" + Character.toUpperCase(field.getName().charAt(0)) + field.getName().substring(1);
            builder = builder.defineMethod(getterName, field.getType(), javax.lang.model.element.Modifier.PUBLIC)
                    .intercept(FieldAccessor.ofField(field.getName()));
        }
        return builder;
    }

    @Override
    public void close() throws Exception {}
}

注册处理器后,编译标注了@MyByteBuddyGetter的类,Byte Buddy会直接在原类中注入getter方法,不需要生成辅助类。

最佳实践

  • 优先选用原生注解处理器:无依赖、兼容性强,适合简单的定制需求。
  • 复杂场景用Google Auto或Byte Buddy:减少样板代码,提供更丰富的代码生成API。
  • 注解保留策略用SOURCE:不需要在运行时保留,降低内存开销。
  • 处理过程中用Messager输出日志/错误:方便调试处理器的执行逻辑。
  • 测试时可通过javac -processor 全类名指定处理器,或在IDE中开启注解处理器支持。

内容的提问来源于stack exchange,提问作者saba artkmeladze

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 21:14:54