在 Lombok 的构建器中使用自定义 setter
Use custom setter in Lombok's builder
我在基于 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);
}
但是当我使用 Lombok 生成的构建器时:
User user = User.builder()
.password(password)
.build();
我的自定义 setter 未被调用,因此密码未被编码。这让我很难过。
我的自定义setter当然是在直接使用的时候调用:
public void changePassword(String password, User user) {
user.setPassword(password);
}
如何让 Lombok 的构建器使用我的自定义 setter?
Per the documentation for @Builder
: 自己定义足够的骨架即可。特别是,Lombok 将生成 class 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;
}
}
}
您使用的是 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());
}
}
结果是:
User(username=This is my username, password=ENCRIYP Password)
我已经接受了 chrylis 的回答,但为了完整起见,这里有一些方法可以最大限度地减少自定义和重复。
自定义 setter 和带静态助手的构建器
静态助手可用于在自定义 User.UserBuilder::password
方法和自定义 User::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);
}
}
自定义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);
}
}
自定义 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);
}
}
不是问题的真正答案,而是一个边缘案例,让我花了很多时间来找到问题。
如果您在具有 Builder.Default
值的字段上具有 Builder
和自定义 setter,则生成的构建器中的字段名称 class 与原始字段不同。事实上,您将有两个字段而不是一个字段:password$value
和 password$set
.
不过,您可以通过这种方式在自定义 setter 中使用它们:
public UserBuilder password(String password) {
this.password$value = ENCODER.encode(password);
this.password$set = true;
return this;
}
棘手的部分是,如果您使用原始字段名称,Intellij IDEA 不会警告您(好像一切都很好,但当然不会编译)。已经向插件提交错误报告。
我在工作中遇到过这种情况。下面是我想出的。这个想法是覆盖 Lombok 的 protected User(User.UserBuilder b)
方法
@Data
@Builder
public class User {
private static final PasswordEncoder ENCODER = new BCryptPasswordEncoder();
private String password;
private Integer otherField;
protected User(final User.UserBuilder<?, ?> b) {
setPassword(b.password);
otherField = b.otherField;
}
public void setPassword(String password) {
Assert.notNull(password);
this.password = ENCODER.encode(password);
}
}
我在基于 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);
}
但是当我使用 Lombok 生成的构建器时:
User user = User.builder()
.password(password)
.build();
我的自定义 setter 未被调用,因此密码未被编码。这让我很难过。
我的自定义setter当然是在直接使用的时候调用:
public void changePassword(String password, User user) {
user.setPassword(password);
}
如何让 Lombok 的构建器使用我的自定义 setter?
Per the documentation for @Builder
: 自己定义足够的骨架即可。特别是,Lombok 将生成 class 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;
}
}
}
您使用的是 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());
}
}
结果是:
User(username=This is my username, password=ENCRIYP Password)
我已经接受了 chrylis 的回答,但为了完整起见,这里有一些方法可以最大限度地减少自定义和重复。
自定义 setter 和带静态助手的构建器
静态助手可用于在自定义 User.UserBuilder::password
方法和自定义 User::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);
}
}
自定义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);
}
}
自定义 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);
}
}
不是问题的真正答案,而是一个边缘案例,让我花了很多时间来找到问题。
如果您在具有 Builder.Default
值的字段上具有 Builder
和自定义 setter,则生成的构建器中的字段名称 class 与原始字段不同。事实上,您将有两个字段而不是一个字段:password$value
和 password$set
.
不过,您可以通过这种方式在自定义 setter 中使用它们:
public UserBuilder password(String password) {
this.password$value = ENCODER.encode(password);
this.password$set = true;
return this;
}
棘手的部分是,如果您使用原始字段名称,Intellij IDEA 不会警告您(好像一切都很好,但当然不会编译)。已经向插件提交错误报告。
我在工作中遇到过这种情况。下面是我想出的。这个想法是覆盖 Lombok 的 protected User(User.UserBuilder b)
方法
@Data
@Builder
public class User {
private static final PasswordEncoder ENCODER = new BCryptPasswordEncoder();
private String password;
private Integer otherField;
protected User(final User.UserBuilder<?, ?> b) {
setPassword(b.password);
otherField = b.otherField;
}
public void setPassword(String password) {
Assert.notNull(password);
this.password = ENCODER.encode(password);
}
}