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

Java编译时实现方法参数动态化:自定义Lombok式注解需求

实现支持Supplier参数的动态方法重载(类似Lombok注解)

针对你的大型Builder类需求,推荐两种可行方案:基于JDK注解处理器(APT)的编译时生成,或者自定义Lombok插件直接修改原类字节码,下面是具体实现步骤:

方案一:自定义注解+APT编译时生成重载方法

这种方案基于标准JDK特性,不依赖Lombok内部API,兼容性更强。

1. 定义@DynamicArguments注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE) // 仅在源码阶段生效
public @interface DynamicArguments {
}

2. 实现注解处理器(用JavaPoet简化代码生成)

使用JavaPoet库生成包含重载方法的Builder子类,自动将非函数式接口参数替换为Supplier<T>:

import com.squareup.javapoet.*;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import java.util.*;

@SupportedAnnotationTypes("com.yourpackage.DynamicArguments")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class DynamicArgumentsProcessor extends AbstractProcessor {
    private Elements elementUtils;
    private Types typeUtils;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        elementUtils = processingEnv.getElementUtils();
        typeUtils = processingEnv.getTypeUtils();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        for (Element element : roundEnv.getElementsAnnotatedWith(DynamicArguments.class)) {
            if (!(element instanceof ExecutableElement method)) continue;
            TypeElement enclosingClass = (TypeElement) method.getEnclosingElement();

            List<ParameterSpec> overloadParams = new ArrayList<>();
            List<CodeBlock> argCalls = new ArrayList<>();

            // 遍历原方法参数,生成重载方法的参数和调用逻辑
            for (VariableElement param : method.getParameters()) {
                TypeMirror paramType = param.asType();
                if (isFunctionalInterface(paramType)) {
                    // 保留原函数式接口参数(比如lambda类型)
                    overloadParams.add(ParameterSpec.builder(paramType, param.getSimpleName().toString()).build());
                    argCalls.add(CodeBlock.of("$N", param.getSimpleName()));
                } else {
                    // 替换为Supplier<T>参数
                    TypeName supplierType = ParameterizedTypeName.get(ClassName.get(Supplier.class), TypeName.get(paramType));
                    String supplierParamName = param.getSimpleName() + "Supplier";
                    overloadParams.add(ParameterSpec.builder(supplierType, supplierParamName).build());
                    argCalls.add(CodeBlock.of("$N.get()", supplierParamName));
                }
            }

            // 构建重载方法体
            MethodSpec.Builder overloadMethodBuilder = MethodSpec.methodBuilder(method.getSimpleName().toString())
                    .addModifiers(method.getModifiers())
                    .returns(TypeName.get(method.getReturnType()))
                    .addParameters(overloadParams);

            if (method.getReturnType().getKind() == TypeKind.VOID) {
                overloadMethodBuilder.addStatement("this.$N($L)", method.getSimpleName(), CodeBlock.join(argCalls, ", "));
            } else {
                overloadMethodBuilder.addStatement("return this.$N($L)", method.getSimpleName(), CodeBlock.join(argCalls, ", "));
            }

            // 生成原Builder的子类,包含重载方法
            TypeSpec dynamicBuilder = TypeSpec.classBuilder(enclosingClass.getSimpleName() + "Dynamic")
                    .superclass(ClassName.get(enclosingClass))
                    .addMethod(overloadMethodBuilder.build())
                    .build();

            try {
                JavaFile.builder(elementUtils.getPackageOf(enclosingClass).getQualifiedName().toString(), dynamicBuilder)
                        .writeTo(processingEnv.getFiler());
            } catch (Exception e) {
                processingEnv.getMessager().printMessage(javax.tools.Diagnostic.Kind.ERROR, "生成动态Builder失败: " + e.getMessage());
            }
        }
        return true;
    }

    // 判断是否为函数式接口(排除lambda类型参数)
    private boolean isFunctionalInterface(TypeMirror type) {
        if (type.getKind() != TypeKind.DECLARED) return false;
        DeclaredType declaredType = (DeclaredType) type;
        TypeElement typeElement = (TypeElement) declaredType.asElement();
        return typeElement.getAnnotation(FunctionalInterface.class) != null;
    }
}

3. 依赖配置(Maven)

添加JavaPoet依赖:

<dependency>
    <groupId>com.squareup</groupId>
    <artifactId>javapoet</artifactId>
    <version>1.13.0</version>
    <scope>provided</scope>
</dependency>

使用方式

编译后会生成原Builder类名Dynamic的子类,你可以用这个子类调用重载方法:

new MyBuilderDynamic().randFunc(() -> "123456", () -> 8);

方案二:自定义Lombok插件(直接修改原类)

如果希望直接在原Builder类中生成重载方法,和Lombok的@Builder等注解体验一致,可以自定义Lombok插件:

1. 实现Lombok注解处理器

import lombok.core.AnnotationHandler;
import lombok.core.HandlerPriority;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Name;

@ProviderFor(AnnotationHandler.class)
@HandlerPriority(HandlerPriority.DEFAULT)
public class DynamicArgumentsHandler extends JavacAnnotationHandler<DynamicArguments> {
    @Override
    public void handle(DynamicArguments annotation, JavacNode annotationNode) {
        JavacNode methodNode = annotationNode.up();
        if (!(methodNode.get() instanceof JCTree.JCMethodDecl method)) return;
        TreeMaker treeMaker = annotationNode.getTreeMaker();

        List<JCTree.JCVariableDecl> overloadParams = List.nil();
        List<JCTree.JCExpression> argCalls = List.nil();

        for (JCTree.JCVariableDecl param : method.params) {
            Type paramType = param.type.type;
            if (isFunctionalInterface(paramType, annotationNode)) {
                overloadParams = overloadParams.append(param);
                argCalls = argCalls.append(treeMaker.Ident(param.name));
            } else {
                // 构建Supplier<T>类型
                Type supplierType = annotationNode.getTypeUtils().getDeclaredType(
                        annotationNode.getTypeUtils().getTypeElement("java.util.function.Supplier"),
                        paramType
                );
                Name supplierParamName = annotationNode.getNames().fromString(param.name + "Supplier");
                JCTree.JCVariableDecl supplierParam = treeMaker.VarDef(
                        treeMaker.Modifiers(List.nil()),
                        supplierParamName,
                        treeMaker.Type(supplierType),
                        null
                );
                overloadParams = overloadParams.append(supplierParam);
                // 生成 supplier.get() 调用
                argCalls = argCalls.append(treeMaker.App(
                        treeMaker.Ident(annotationNode.getNames().fromString("get")),
                        treeMaker.Ident(supplierParamName),
                        List.nil()
                ));
            }
        }

        // 构建方法体
        JCTree.JCStatement body;
        if (method.restype.type.getKind() == Type.Kind.VOID) {
            body = treeMaker.Exec(treeMaker.App(
                    treeMaker.Ident(method.name),
                    treeMaker.Ident(annotationNode.getNames().fromString("this")),
                    argCalls
            ));
        } else {
            body = treeMaker.Return(treeMaker.App(
                    treeMaker.Ident(method.name),
                    treeMaker.Ident(annotationNode.getNames().fromString("this")),
                    argCalls
            ));
        }

        // 创建重载方法并添加到原类
        JCTree.JCMethodDecl overloadMethod = treeMaker.MethodDef(
                method.mods,
                method.name,
                method.restype,
                method.typeparams,
                overloadParams,
                method.thrown,
                treeMaker.Block(0, List.of(body)),
                method.defaultValue
        );

        JavacNode classNode = methodNode.up();
        ((JCTree.JCClassDecl) classNode.get()).defs = ((JCTree.JCClassDecl) classNode.get()).defs.append(overloadMethod);
        classNode.rebuild();
    }

    private boolean isFunctionalInterface(Type type, JavacNode node) {
        if (type.getKind() != Type.Kind.INTERFACE) return false;
        TypeElement typeElement = (TypeElement) node.getTypeUtils().asElement(type);
        return typeElement.getAnnotation(FunctionalInterface.class) != null;
    }
}

2. 打包Lombok插件

  • 将上述类打包成JAR
  • 在JAR的META-INF/services/lombok.core.AnnotationHandler文件中添加类的全限定名(比如com.yourpackage.DynamicArgumentsHandler)

3. 项目中使用

  • 引入该插件JAR
  • 在项目根目录添加lombok.config文件:
    lombok.config.stopBubbling = true
    lombok.extensionMethod = com.yourpackage.DynamicArgumentsHandler
    
  • 给Builder方法添加@DynamicArguments注解后,编译时会自动在原类中生成重载方法,直接调用即可:
    new MyBuilder().randFunc(() -> "123456", () -> 8);
    

方案对比

方案优点缺点
APT生成子类基于标准JDK,兼容性好,无Lombok依赖需要使用生成的子类,不能直接修改原类
Lombok插件直接修改原类,使用体验和原生Lombok一致依赖Lombok内部API,版本兼容性可能受影响

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

相关产品推荐
方舟 Agent Plan

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

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