lombok的@Builder注解原理背后干了啥?

2021-02-23 14:41:32 浏览数 (1)

  • 在Lombok v0.12.0中作为实验功能引入
  • 自v1.16.0起获得了 @Singular 支持并被升级到了主要lombok软件包
  • @Builder、@Singular自lombok v1.16.8起,使用可以添加明确的方法
  • @Builder.Default* v1.16.16中添加了功能
  • @Builder(builderMethodName = “”) 从=1.18.8开始是合法的(并且将抑制生成器方法的生成)
  • @Builder(access = AccessLevel.PACKAGE) 从lombok v1.18.8开始是合法的(并将生成具有指定访问级别的构建器类,构建器方法等)

功能

@Builder注解为你的类提供复杂的建造者模式 API。

@Builder 使你可以自动生成使您的类可实例化的代码,例如:

代码语言:javascript复制
Person.builder()
	.name("AdamSavage")
	.city("SanFrancisco")
	.job("Mythbusters")
	.job("Unchained Reaction")
	.build();

使用位置

@Builder可以放在类,构造器或方法上。虽然“基于类”和“基于构造器”模式是最常见的用例,但使用“方法”用例最容易解释。

代码语言:javascript复制
public class ResponseMessage extends Message<OperationResult> {
    private ResponseMessage(MessageHeader messageHeader, OperationResult messageBody) {
        super(messageHeader, messageBody);
    }

    public Class getMessageBodyDecodeClass(int opcode) {
        return OperationType.fromOpCode(opcode).getOperationResultClazz();
    }

    public static ResponseMessage.ResponseMessageBuilder builder() {
        return new ResponseMessage.ResponseMessageBuilder();
    }

    public ResponseMessage() {
    }

    public static class ResponseMessageBuilder {
        private MessageHeader messageHeader;
        private OperationResult messageBody;

        ResponseMessageBuilder() {
        }

        public ResponseMessage.ResponseMessageBuilder messageHeader(MessageHeader messageHeader) {
            this.messageHeader = messageHeader;
            return this;
        }

        public ResponseMessage.ResponseMessageBuilder messageBody(OperationResult messageBody) {
            this.messageBody = messageBody;
            return this;
        }

        public ResponseMessage build() {
            return new ResponseMessage(this.messageHeader, this.messageBody);
        }

        public String toString() {
            return "ResponseMessage.ResponseMessageBuilder(messageHeader="   this.messageHeader   ", messageBody="   this.messageBody   ")";
        }
    }
}

被**@Builder**注解的方法(从现在开始称为target)将生成以下7件事: 即构造内部类,在内部类赋值属性,build时调用含有所有属性的构造方法创建对象。

  1. 一个内部静态类,名为FooBuilder,其类型参数与静态方法相同(称为builder)
  2. 在构建器中:目标的每个参数有一个private非static 非 final 字段
  3. 在builder中:包私有的无参数空构造器
  4. 在builder中:对目标的每个参数使用类似 setter 的方法:与该参数具有相同的类型和相同的名称。它返回构建器本身,以便可以将setter调用链接起来
  5. 在builder中:build()调用该方法的方法,并在每个字段中传递。它返回与目标返回相同的类型
  6. 有意义的toString()实现
  7. 在包含target的类中:一个builder()方法,该方法创建builder的新实例

如果该元素已存在,则每个列出的生成元素都将被静默跳过(忽略参数计数并仅查看名称)。这包括构建器本身:如果该类已经存在,则lombok会简单地开始在此现有类中注入字段和方法,除非要注入的字段/方法当然已经存在。但是,您不能在生成器类上放置生成lombok批注的任何其他方法(或构造函数)。例如,您不能放入@EqualsAndHashCodebuilder类。 @Builder可以为收集参数/字段生成所谓的“奇异”方法。它们采用1个元素而不是整个列表,然后将该元素添加到列表中。例如:Person.builder().job(“Mythbusters”).job(“Unchained Reaction”).build();将导致该List jobs字段中包含2个字符串。要获得此行为,必须使用注释字段/参数@Singular。该功能具有其自己的文档。

现在,“方法”模式已经很清楚了,@Builder在构造函数上添加注释的功能类似。实际上,构造函数只是具有特殊语法以调用它们的静态方法:它们的“返回类型”是它们构造的类,并且它们的类型参数与类本身的类型参数相同。

应用于@Builder类就像是将其添加@AllArgsConstructor(access = AccessLevel.PACKAGE)到该类并将@Builder注释应用于此all-args-constructor一样。仅当您自己未编写任何显式构造函数时,此方法才有效。如果确实有显式构造函数,则将@Builder注释放在构造函数上而不是在类上。请注意,如果将@Value和@Builder都放在类上,则@Builder要生成“ wins”的程序包私有构造函数,而禁止@Value要生成的构造函数。

如果@Builder用于生成生成器来生成您自己的类的实例(除非添加@Builder到不返回您自己的类型的方法中,否则通常都是这种情况),您还可以@Builder(toBuilder = true)在类中使用生成实例方法toBuilder();它创建一个新的构建器,该构建器以该实例的所有值开始。您可以将@Builder.ObtainVia注释放在参数(对于构造函数或方法的情况)或字段(对于@Builder类型的情况)上,以指示从该实例获取该字段/参数的值的替代方法。例如,您可以指定要调用的方法:@Builder.ObtainVia(method = “calculateFoo”)。

builder类的名称为FoobarBuilder,其中Foobar是目标的返回类型的简化的,以标题区分大小写的形式-即,@Builderon构造函数和类型的类型名称,以及@Builderon方法的返回类型的名称。。例如,如果@Builder应用于名为的类com.yoyodyne.FancyList,则构建器名称将为FancyListBuilder。如果@Builder将应用于返回的方法,void则将命名构建器VoidBuilder。

构建器的可配置方面包括:

该生成器的类名(默认:返回类型 “生成器”) 该版本()方法的名称(默认:“build”) 该生成器()方法的名称(默认:“builder”) 如果需要toBuilder()(默认值:否) 所有生成的元素的访问级别(默认值:)public。 (不推荐使用)如果您希望构建器的“ set”方法具有前缀,即Person.builder().setName(“Jane”).build()而不是前缀,Person.builder().name(“Jane”).build()则应为前缀。 用法示例,其中所有选项均从其默认值更改: @Builder(builderClassName = “HelloWorldBuilder”, buildMethodName = “execute”, builderMethodName = “helloWorld”, toBuilder = true, access = AccessLevel.PRIVATE, setterPrefix = “set”) 想要将构建器与JSON / XML工具Jackson一起使用?我们涵盖了:检查@Jacksonized功能。

子类如何使用 @Build 注解?

  • 父类
  • 子类

同时在子类和全参数的构造器使用 @Builder 注解,最终的 build() 函数只返回了空参的构造器创建的一个子类对象,因此属性“采用 builder 方式设置的 字段最终都丢失了。

如果成员被注解,则必须是构造器或方法。如果对类注解,则会生成一个private构造器,并将所有字段作为参数,就像在类上存在 @AllArgsConstructor(AccessLevel.PRIVATE) ,就好像该构造器已经存在而是用@Builder注解。

将其加到类上,相当于包含所有属性的私有构造器,且构造器加上 @Builder 注解。

参考

  • https://projectlombok.org/features/Builder

0 人点赞