Use custom setter in Lombok's builder

Solution 1:

Per the documentation for @Builder: Just define enough skeleton yourself. In particular, Lombok will generate a class UserBuilder, fields mirroring the User fields, and builder methods, and you can provide any or all of this yourself.

@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;
        }
    }
}

Solution 2:

I've accepted chrylis's answer but for completeness, here's are some ways to minimize customization and duplication.

Custom setter and builder with static helper

A static helper can be used to shares most of the set password functionality across the custom User.UserBuilder::password method and the custom User::setPassword method:

@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);
    }
}

Custom setter and constructor

A custom constructor can use User::setPassword which is invoked by the Lombok generated 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);
    }
}

Custom setter and constructor with static helper

Or, a little more elegantly, with a custom constructor and a static helper method:

@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);
    }
}