在Lombok的构建器中使用自定义setter

Jan*_*sen 28 java lombok

我在基于Lombok的POJO中有一个自定义setter:

@Data
@Builder
public class User {
    private static final PasswordEncoder ENCODER = new BCryptPasswordEncoder();

    private String password = null;

    public void setPassword(String password) {
        Assert.notNull(password);
        this.password = ENCODER.encode(password);
    }
Run Code Online (Sandbox Code Playgroud)

但是当我使用Lombok生成的构建器时:

User user = User.builder()
    .password(password)
    .build();
Run Code Online (Sandbox Code Playgroud)

我的自定义setter未被调用,因此密码未被编码.这让我很伤心.

当然,直接使用我的自定义setter时会调用它:

public void changePassword(String password, User user) {
    user.setPassword(password);
}
Run Code Online (Sandbox Code Playgroud)

如果让Lombok的构建器使用我的自定义setter,我该怎么办?

chr*_*ke- 47

根据以下文档@Builder:自己定义足够的骨架.特别是,Lombok将生成一个类UserBuilder,镜像User字段的字段和构建器方法,您可以自己提供任何或所有这些.

@Builder
public class User {
    private static final PasswordEncoder ENCODER = new BCryptPasswordEncoder();

    private String username;

    private String password;

    public static class UserBuilder {
        public UserBuilder password(String password) {
            this.password = ENCODER.encode(password);
            return this;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 对于像我这样咖啡因含量低且不明白为什么这段代码不起作用的人来说:不要定义“UserBuilder”类,而是定义一个 <NameOfTheParentClass>Builder。我读得有点快,我认为这个名称是 Lombok 中创建“用户提供的构建器”的标准名称。 (5认同)
  • 谢谢,**chrylis** - 但是`Person :: setPassword`(来自Lombok的`@Data`)和`Person.PersonBuilder :: password`如何分享DRY实现? (2认同)
  • 当使用“@SuperBuilder”而不是“@Builder”时,我在“this.password”上收到_无法解析符号密码_错误:我通过使用[此答案](https://stackoverflow.com/)中解释的方法解决了它a/63861259/334569)。 (2认同)

Jan*_*sen 7

我接受了chrylis的回答,但为了完整起见,这里有一些方法可以最大限度地减少定制和复制.

使用静态助手的自定义setter和builder

可以使用静态帮助程序在自定义方法和自定义方法之间共享大多数设置密码功能:User.UserBuilder::passwordUser::setPassword

@Data
@Builder
public class User {
    private static final PasswordEncoder ENCODER = new BCryptPasswordEncoder();

    private String password = null;

    public void setPassword(String password) {
        this.password = _encodePassword(password);
    }

    public static class UserBuilder {
        public UserBuilder password(String password) {
            this.password = _encodePassword(password)
            return this;
        }
    }

    private static String _encodePassword(String password) {
        Assert.notNull(password);
        return ENCODER.encode(password);
    }
}
Run Code Online (Sandbox Code Playgroud)

自定义setter和构造函数

可以使用User::setPassword由生成的Lombok调用的自定义构造函数User.UserBuilder::build():

@Data
@Builder
public class User {
    private static final PasswordEncoder ENCODER = new BCryptPasswordEncoder();

    private String password = null;

    User(String password) {
        setPassword(password);
    }

    public void setPassword(String password) {
        Assert.notNull(password);
        this.password = ENCODER.encode(password);
    }
}
Run Code Online (Sandbox Code Playgroud)

使用静态助手的自定义setter和构造函数

或者,更优雅一点,使用自定义构造函数和静态帮助器方法:

@Data
@Builder
public class User {
    private static final PasswordEncoder ENCODER = new BCryptPasswordEncoder();

    private String password = null;

    User(String password) {
        _encodePassword(password, this);
    }

    public void setPassword(String password) {
        _encodePassword(password, this);
    }

    private static _encodePassword(String password, User user) {
        Assert.notNull(password);
        user.password = ENCODER.encode(password);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 信息非常丰富。不幸的是, _Custom setter 和 constructor_ 示例的构造函数需要采用与类的字段一样多的参数,这可能会变得相当混乱。 (2认同)

Boj*_*vic 5

您正在使用 setPassword 而不是构建器的 set 方法。

这是对我有用的:

import lombok.Builder;
import lombok.Data;

@Builder
@Data
public class User {
    private String username;
    private String password;

    public static class UserBuilder {
        private String password;
        public UserBuilder password(String password ) {
            this.password ="ENCRIYP " +  password;
            return this;
        }
    }

    public static void main(String[] args) {
        System.out.println(User.builder().username("This is my username").password("Password").build().toString());

    }
}
Run Code Online (Sandbox Code Playgroud)

结果是: User(username=This is my username, password=ENCRIYP Password)


Rad*_*Rad 5

这并不是问题的答案,而是一个边缘案例,让我花了相当长的时间来找到问题。
如果您Builder在具有值的字段上使用自定义设置器Builder.Default,则生成的构建器类中的字段名称与原始字段不同。事实上,您将有两个字段而不是一个:password$valuepassword$set
不过,您可以通过以下方式在自定义设置器中使用它们:

public UserBuilder password(String password) {
  this.password$value = ENCODER.encode(password);
  this.password$set = true;
  return this;
}
Run Code Online (Sandbox Code Playgroud)

棘手的部分是,如果您使用原始字段名称,Intellij IDEA 不会警告您(好像一切都很好,但当然不会编译)。已经向插件提交了错误报告。