写点什么

听说 Lombok 的 @Builder 不好使?快来试试这个,rocketmq 原理面试题

用户头像
极客good
关注
发布于: 刚刚

.itemId(6542744309L)


.itemTitle("测试请不要拍小番茄 500g/盒")


.price(500L)


.promotionPrice(325L)


.build();


System.out.println(itemDTO);


这样写不但美观,而且还会省去好多无用的代码。


Builder 注解的使用限制


==================


当我们的实体对象有继承的设计的时候,Builder 注解就没那么好用了,还是以商品实体为例,如果现在商品类都继承自一个 BaseDTO


@Builder


@NoArgsConstructor


public class BaseDTO {


/**


  • 业务身份


*/


private String bizType;


/**


  • 场景


*/


private String scene;


}


这时候我们再使用 Builder 注解就会发现,在子类中无法通过 builder 方法构造父类中的成员变量



给 BaseDTO 上加上 Builder 注解也不会有任何效果。事实上,Builder 注解只管承接注解的这个类,而不会管他的父类或者子类。如果真的是这样的话,遇到有继承的类,只好又打回原形,写一堆的 setter 方法了。


试试 SuperBuilder 吧


===================


这个问题在 lombokv1.18.2 版本之前其实很难办,但是在这个版本官方引入了一个新的注解 @SuperBuilder,无法 build 父类的问题迎刃而解


The @SuperBuilder annotati


【一线大厂Java面试题解析+核心总结学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


on produces complex builder APIs for your classes. In contrast to @Builder, @SuperBuilder also works with fields from superclasses. However, it only works for types. Most importantly, it requires that?all superclasses?also have the@SuperBuilder annotation.


按照官方文档的说法,为了能够使用 build 方法,只需要在子类和父类上都加 @SuperBuilder 注解,我们试一下



果然现在就可以在子类的实例中 build`父类的成员变量了


Lombok 的原理


=============


Lombok 自动生成代码的实现也是依赖于 JVM 开放的扩展点,使其可以在编译的时候修改抽象语法树,从而影响最终生成的字节码



图片来源地址:http://notatube.blogspot.com/2010/12/project-lombok-creating-custom.html


为什么 Builder 不能处理父类的成员变量


=========================


我们可以翻一下 Lombok 的源码,Lombok 对所有的注解都有两套实现,javac 和 eclipse,由于我们的运行环境是 Idea 所以我们选择 javac 的实现,javac 版本的实现在 lombok.javac.handlers.HandleBuilder#handle 这个方法中


JavacNode parent = annotationNode.up();


if (parent.get() instanceof JCClassDecl) {


job.parentType = parent;


JCClassDecl td = (JCClassDecl) parent.get();


ListBuffer<JavacNode> allFields = new ListBuffer<JavacNode>();


boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation("lombok.experimental.Value", parent));


// 取出所有的成员变量


for (JavacNode fieldNode : HandleConstructor.findAllFields(parent, true)) {


JCVariableDecl fd = (JCVariableDecl) fieldNode.get();


JavacNode isDefault = findAnnotation(Builder.Default.class, fieldNode, false);


boolean isFinal = (fd.mods.flags & Flags.FINAL) != 0 || (valuePresent && !hasAnnotation(NonFinal.class, fieldNode));


// 巴拉巴拉,省略掉


}


这里的 annotationNode 就是 Builder 注解,站在抽象语法树的角度,调用 up 方法得到的就是被注解修饰的类,也就是需要生成 builder 方法的类。


通过查看源代码,@Builder 注解是可以修饰类,构造函数和方法的,为了简单起见,上面的代码只截取了 @Builder 修饰类这一种情况,这段代码关键的地方就在于调用 HandleConstructor.findAllFields 方法获得类中所有的成员变量:


public static List<JavacNode> findAllFields(JavacNode typeNode, boolean evenFinalInitialized) {


ListBuffer<JavacNode> fields = new ListBuffer<JavacNode>();


// 从抽象语法树出发,遍历类的所有的成员变量


for (JavacNode child : typeNode.down()) {


if (child.getKind() != Kind.FIELD) continue;


JCVariableDecl fieldDecl = (JCVariableDecl) child.get();


//Skip fields that start with $


if (fieldDecl.name.toString().startsWith("$")) continue;


long fieldFlags = fieldDecl.mods.flags;


//Skip static fields.


if ((fieldFlags & Flags.STATIC) != 0) continue;


//Skip initialized final fields


boolean isFinal = (fieldFlags & Flags.FINAL) != 0;


if (evenFinalInitialized || !isFinal || fieldDecl.init == null) fields.append(child);


}


return fields.toList();


}

用户头像

极客good

关注

还未添加个人签名 2021.03.18 加入

还未添加个人简介

评论

发布
暂无评论
听说Lombok的@Builder不好使?快来试试这个,rocketmq原理面试题