如何实现类似Lombok @Getter的替代值获取功能?
解决方案:字段为null时取备选字段值的实现
一、直接手动实现(简单高效)
如果只是个别字段需要这个逻辑,没必要折腾自定义注解,直接在DTO中手动编写方法即可:
@Getter @NoArgsConstructor @AllArgsConstructor @ToString public class SomeDto extends BaseDto { private BigDecimal a; private BigDecimal b; // 自定义方法,当b为null时返回a的值 public BigDecimal getBOrAlternative() { return Objects.nonNull(b) ? b : a; } }
直接调用dto.getBOrAlternative()就能满足需求,无需额外配置。
二、自定义Lombok注解+处理器(复用场景)
如果多个字段需要类似逻辑,可以通过自定义Lombok注解实现自动生成方法,解决你之前未成功的核心问题——针对字段级别处理:
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 GetOrAlternative { String alternative(); // 配置备选字段名 }
2. 编写Lombok注解处理器(字段级别处理)
需要使用Lombok的AbstractAnnotationHandler来操作编译期AST(抽象语法树),避免和@Getter等注解冲突:
import lombok.core.AnnotationValues; import lombok.core.handlers.HandlerUtil; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; import org.mangosdk.spi.ProviderFor; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCReturn; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.util.List; @ProviderFor(JavacAnnotationHandler.class) public class HandleGetOrAlternative extends JavacAnnotationHandler<GetOrAlternative> { @Override public void handle(AnnotationValues<GetOrAlternative> annotation, JCTree.JCAnnotation ast, JavacNode node) { if (node.getKind() != ElementKind.FIELD) { node.addError("@GetOrAlternative只能用于字段"); return; } JCVariableDecl field = (JCVariableDecl) node.get(); String alternativeField = annotation.getInstance().alternative(); String fieldName = field.name.toString(); // 生成方法名:get + 首字母大写字段名 + OrAlternative String methodName = "get" + capitalize(fieldName) + "OrAlternative"; JavacTreeMaker treeMaker = node.getTreeMaker(); // 构建方法体:return this.b != null ? this.b : this.a; JCExpression fieldAccess = treeMaker.Select(treeMaker.Ident(node.toName("this")), node.toName(fieldName)); JCExpression altFieldAccess = treeMaker.Select(treeMaker.Ident(node.toName("this")), node.toName(alternativeField)); JCExpression condition = treeMaker.Binary(com.sun.tools.javac.code.TypeTags.NE, fieldAccess, treeMaker.Literal(null)); JCExpression ternary = treeMaker.Conditional(condition, fieldAccess, altFieldAccess); JCBlock body = treeMaker.Block(0, List.of(treeMaker.Return(ternary))); // 构建方法声明 JCMethodDecl method = treeMaker.MethodDef( treeMaker.Modifiers(Flags.PUBLIC), node.toName(methodName), field.vartype, List.nil(), List.nil(), List.nil(), body, null ); // 将方法注入到类中 HandlerUtil.injectMethod(node.up(), method); } private String capitalize(String str) { if (str == null || str.isEmpty()) return str; return str.substring(0, 1).toUpperCase() + str.substring(1); } }
3. 注册处理器
在项目的src/main/resources/META-INF/services目录下创建文件javax.annotation.processing.Processor,写入处理器全类名:
com.yourpackage.HandleGetOrAlternative
4. 使用注解
回到你的DTO类,即可正常使用注解:
@Getter @NoArgsConstructor @AllArgsConstructor @ToString public class SomeDto extends BaseDto { private BigDecimal a; @GetOrAlternative(alternative = "a") private BigDecimal b; }
编译后会自动生成getBOrAlternative()方法,调用逻辑和手动实现一致。
为什么之前自定义注解失败?
你之前的问题大概率是没有使用Lombok的AST处理机制,而是用了普通的Java注解处理器直接生成源文件,导致和@Getter等Lombok注解的编译期逻辑冲突,且没有正确针对字段级别注入方法。使用Lombok官方的AbstractAnnotationHandler能完美适配Lombok的编译流程,实现字段级别的方法生成。
内容的提问来源于stack exchange,提问作者김효성
相关产品推荐
相关产品推荐

