前端视角 Java Web 入门手册 3.5:常用工具—— lombok

107 阅读4分钟

Lombok 是一个 Java 库,通过注解处理器在编译时自动生成常用的代码,如 getter/setter 方法、构造函数、toString 方法、equals 和 hashCode 方法等。这样开发者无需手动编写这些重复性的模板代码,从而专注于业务逻辑的实现

Lombok 利用 Java 的注解处理器(Annotation Processor)在编译阶段修改抽象语法树(AST),添加必要的代码。这些代码在编译后的字节码中存在,但在源代码中隐藏,从而保持代码的简洁性。

常用注解

@Getter 和 @Setter

自动为类的字段生成 getter 和 setter 方法

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class User {
    private String name;
    private int age;
}

编译后生成代码

public class User {
    private String name;
    private int age;

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return this.age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

@ToString

生成 toString 方法,默认包括所有字段

import lombok.ToString;
import lombok.Getter;

@ToString
@Getter
public class User {
    private String name;
    private int age;
}

编译后的代码

public class User {
    private String name;
    private int age;

    public String getName() { return this.name; }
    public int getAge() { return this.age; }

    @Override
    public String toString() {
        return "User(name=" + this.getName() + ", age=" + this.getAge() + ")";
    }
}

@EqualsAndHashCode

自动生成 equals 和 hashCode 方法,用于比较对象

import lombok.EqualsAndHashCode;
import lombok.Getter;

@EqualsAndHashCode
@Getter
public class User {
    private String name;
    private int age;
}

编译后的代码

public class User {
    private String name;
    private int age;

    public String getName() { return this.name; }
    public int getAge() { return this.age; }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof User)) return false;
        User user = (User) o;
        return this.getAge() == user.getAge() &&
               java.util.Objects.equals(this.getName(), user.getName());
    }

    @Override
    public int hashCode() {
        return java.util.Objects.hash(this.getName(), this.getAge());
    }
}

@NoArgsConstructor、@AllArgsConstructor 和 @RequiredArgsConstructor

自动生成不同类型的构造函数

import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.Getter;

@NoArgsConstructor
@AllArgsConstructor
@RequiredArgsConstructor
@Getter
public class User {
    private String name;
    private final int age;
}

编译后的代码

public class User {
    private String name;
    private final int age;

    // 无参构造函数
    public User() {
    }

    // 全参构造函数
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 必填字段的构造函数(final 或 @NonNull)
    public User(int age) {
        this.age = age;
    }

    public String getName() { return this.name; }
    public void setName(String name) { this.name = name; }
    public int getAge() { return this.age; }
}

@Data

综合应用多个注解,生成 getter/setter、toStringequalshashCode 和所有字段的构造函数。

import lombok.Data;

@Data
public class User {
    private String name;
    private int age;
}

编译后的代码

public class User {
    private String name;
    private int age;

    // Getter 和 Setter
    public String getName() { return this.name; }
    public void setName(String name) { this.name = name; }
    public int getAge() { return this.age; }
    public void setAge(int age) { this.age = age; }

    // toString 方法
    @Override
    public String toString() {
        return "User(name=" + this.getName() + ", age=" + this.getAge() + ")";
    }

    // equals 和 hashCode 方法
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof User)) return false;
        User user = (User) o;
        return this.getAge() == user.getAge() &&
               java.util.Objects.equals(this.getName(), user.getName());
    }

    @Override
    public int hashCode() {
        return java.util.Objects.hash(this.getName(), this.getAge());
    }
}

@Builder

提供建造者模式(Builder Pattern),便于创建复杂对象

import lombok.Builder;

@Builder
public class User {
    private String name;
    private int age;
    private String email;
}

编译后的代码

public class User {
    private String name;
    private int age;
    private String email;

    private User(UserBuilder builder) {
        this.name = builder.name;
        this.age = builder.age;
        this.email = builder.email;
    }

    public static UserBuilder builder() {
        return new UserBuilder();
    }

    // Builder 类
    public static class UserBuilder {
        private String name;
        private int age;
        private String email;

        UserBuilder() {
        }

        public UserBuilder name(String name) {
            this.name = name;
            return this;
        }

        public UserBuilder age(int age) {
            this.age = age;
            return this;
        }

        public UserBuilder email(String email) {
            this.email = email;
            return this;
        }

        public User build() {
            return new User(this);
        }

        public String toString() {
            return "User.UserBuilder(name=" + this.name + ", age=" + this.age + ", email=" + this.email + ")";
        }
    }

    // Getter 和 Setter
    public String getName() { return this.name; }
    public void setName(String name) { this.name = name; }
    public int getAge() { return this.age; }
    public void setAge(int age) { this.age = age; }
    public String getEmail() { return this.email; }
    public void setEmail(String email) { this.email = email; }
}

使用方式

public class Main {
    public static void main(String[] args) {
        User user = User.builder()
                        .name("Alice")
                        .age(30)
                        .email("alice@example.com")
                        .build();
        System.out.println(user);
    }
}

输出

User(name=Alice, age=30, email=alice@example.com)

@Slf4j

自动为类生成一个 org.slf4j.Logger 日志对象

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class UserService {
    public void createUser() {
        log.info("Creating a new user...");
        // 业务逻辑
    }
}

编译后的代码

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class UserService {
    private static final Logger log = LoggerFactory.getLogger(UserService.class);

    public void createUser() {
        log.info("Creating a new user...");
        // 业务逻辑
    }
}

使用 Lombok

添加 maven 依赖

<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.18.24</version>
  <scope>provided</scope>
</dependency>

在 IntelliJ 2020.3 版本后 lombok plugin 已经被内置,无需额外安装

在许多 Java 项目中,尤其是使用 ORM 的项目,实体类通常包含大量的 getter、setter、构造函数,Lombok 可以显著简化实体类、DTO 的编写

不要滥用 lombok

尽管 Lombok 提供了显著的优势,如减少样板代码和提高开发效率,但在实践中,有些代码规约和开发团队选择不推荐或限制 Lombok 的使用

  • 代码隐藏:自动生成的方法(如 getters、setters、构造函数等)在源代码中不可见,可能导致开发者对类的实际行为产生误解。
  • 调试复杂性:调试时,IDE 可能无法直接显示 Lombok 生成的代码,增加了追踪问题的难度。
  • 文档不全:由于 Lombok 生成的代码在源代码中不可见,自动文档工具可能遗漏重要的方法和类信息。
  • 自动化工具依赖:某些工具可能依赖于具体的代码实现,如果 Lombok 生成的代码不符合预期,可能会导致工具失败或生成错误的输出。

Java 14 引入的 Records 提供了一种简洁的方式来定义不可变的数据类,无需手动编写 getter、equals、hashCode 和 toString 方法,如果数据类主要用于存储不可变的属性,Records 是一个理想的选择

定义类

public record User(Long id, String name, String email) {}

使用

public class Main {
    public static void main(String[] args) {
        User user = new User(1L, "Alice", "alice@example.com");
        System.out.println(user);

        // 访问 Record 的字段
        System.out.println("Name: " + user.name());
        System.out.println("Email: " + user.email());
    }
}

输出

User[id=1, name=Alice, email=alice@example.com]
Name: Alice
Email: alice@example.com