侧边栏壁纸
博主头像
Setyg的博客

当秋风染红落叶的时候,就是新生~

  • 累计撰写 86 篇文章
  • 累计创建 66 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

Lombok 学习实践

Setyg
2026-09-14 / 0 评论 / 0 点赞 / 6 阅读 / 0 字

Lombok 学习实践

一、Lombok 简介

Lombok 是一个 Java 库,能自动插入到编辑器和构建工具中,简化 Java 开发。通过添加注解的方式,不再需要为类编写 getter/setter、equals、hashCode、toString 等方法,同时可以自动生成日志变量。

简而言之:Lombok 能以简单的注解形式来简化 Java 代码,提高开发人员的开发效率。

二、Lombok 的使用

使用 Lombok 需要的开发环境:Java + Maven + IntelliJ IDEA 或 Eclipse。

2.1 添加 Maven 依赖

推荐写法(Spring Boot 项目,版本交给 BOM 统一管理):

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

非 Spring Boot 的普通 Maven 项目需要显式指定版本:

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

关于版本:上面的 1.18.4 发布于 2018 年,已经明显落后。Lombok 与 JDK 版本强绑定,旧版本在新 JDK 上会直接编译失败 —— 支持 JDK 17 需要 1.18.22 以上,支持 JDK 21 需要 1.18.30 以上。新项目请使用当前较新的版本,不要照抄这个数字。

scope 写 provided 或 optional 都可以,本质是不要让 Lombok 传递到下游依赖,因为它只在编译期起作用。

2.2 安装插件

Lombok 需要 IDE 插件的配合,否则 IDE 会误报「找不到 getter/setter 方法」。这里以 IDEA 为例说明:

  • IntelliJ IDEA 2020.3 及以上:Lombok 插件已内置,无需手动安装,装好即可用。

  • 更早版本:打开 IDEA 设置 → Plugins → Browse repositories,在弹出窗口中搜索 lombok,然后安装并重启即可。

使用 Eclipse、MyEclipse 的请自行查阅对应的安装方法。

2.3 解决编译时出错问题

如果编译报错、提示找不到符号,通常是没有开启注解处理器。路径为:

Settings → Build, Execution, Deployment → Compiler → Annotation Processors
勾选 Enable annotation processing

设置完成之后程序即可正常运行。注意:新版 IDEA 默认已开启该项,如果被手动关掉,反而会出现「代码没红但是编译不过」的怪现象。

2.4 示例:用与不用的区别

创建一个用户类,先看看不使用 Lombok 的写法:

import java.io.Serializable;
import java.util.Objects;

public class User implements Serializable {

    private static final long serialVersionUID = -8054600833969507380L;
    private Integer id;
    private String username;
    private Integer age;

    public User() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Integer getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        User user = (User) o;
        return Objects.equals(id, user.id) &&
                Objects.equals(username, user.username) &&
                Objects.equals(age, user.age);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, username, age);
    }
}

使用 Lombok 之后,等价的代码只有这么几行:

import lombok.Data;

import java.io.Serializable;

@Data
public class User implements Serializable {

    private static final long serialVersionUID = -8054600833969507380L;
    private Integer id;
    private String username;
    private Integer age;
}

近 60 行样板代码被压缩成 4 个字段声明。需要强调的是:生成的代码只存在于字节码中,源码里看不到,可以用 javap -p User.class 反编译验证,getter/setter/equals/hashCode 都真实存在。

2.5 常用注解

注解

作用位置

生成内容

@Setter

类或字段

注解在类上时为所有字段生成 setter 方法;注解在字段上时只为该字段生成

@Getter

类或字段

使用方法同上,区别在于生成的是 getter 方法

@ToString

类

添加 toString 方法

@EqualsAndHashCode

类

生成 hashCode 和 equals 方法

@NoArgsConstructor

类

生成无参的构造方法

@RequiredArgsConstructor

类

为类中需要特殊处理的字段生成构造方法,比如 final 和被 @NonNull 注解的字段

@AllArgsConstructor

类

生成包含类中所有字段的构造方法

@Data

类

生成 setter/getter、equals、canEqual、hashCode、toString 方法;如为 final 属性,则不会为该属性生成 setter 方法

@Slf4j

类

生成 log 变量,严格意义上是常量:private static final Logger log = LoggerFactory.getLogger(UserController.class);

除此之外,还有一些日常很常用的注解:

注解

用途

@Builder

生成链式建造者,替代冗长的多参数构造

@Value

不可变类的 @Data:类与字段都是 final、无 setter

@NonNull

生成空值检查,为 null 时抛 NullPointerException

@Cleanup

自动关闭资源,等效于 try-with-resources

@SneakyThrows

悄悄抛出受检异常,免写 try-catch(慎用)

@Accessors(chain = true)

setter 返回 this,支持链式赋值

三、Lombok 的工作原理

在使用 Lombok 的过程中,只需要添加相应的注解,无需再为此写任何代码。自动生成的代码到底是如何产生的呢?核心之处就是对于注解的解析。

JDK 5 引入注解的同时,也提供了两种解析方式。

3.1 运行时解析

运行时能够解析的注解,必须将 @Retention 设置为 RUNTIME,这样就可以通过反射拿到该注解。java.lang.reflect 反射包中提供了一个接口 AnnotatedElement,该接口定义了获取注解信息的几个方法,Class、Constructor、Field、Method、Package 等都实现了该接口,对反射熟悉的朋友应该都很熟悉这种解析方式。

但 Lombok 走的不是这条路 —— 它的注解保留策略是 SOURCE,连字节码里都不保留。

3.2 编译时解析

编译时解析有两种机制:

  1. Annotation Processing Tool(apt):自 JDK 5 产生,JDK 7 已标记为过期、不推荐使用,JDK 8 中已彻底删除。自 JDK 6 开始,可以使用 Pluggable Annotation Processing API 来替换它。apt 被替换主要有 2 点原因:

    • API 都在 com.sun.mirror 非标准包下;

    • 没有集成到 javac 中,需要额外运行。

  2. Pluggable Annotation Processing API:JSR 269 自 JDK 6 加入,作为 apt 的替代方案。它解决了 apt 的上述两个问题 —— javac 在执行的时候会调用实现了该 API 的程序,这样我们就可以对编译器做一些增强。

3.3 Lombok 的具体流程

Lombok 本质上就是一个实现了「JSR 269 API」的程序。在使用 javac 的过程中,它产生作用的具体流程如下:

  1. javac 对源代码进行分析,生成一棵抽象语法树(AST);

  2. 运行过程中调用实现了「JSR 269 API」的 Lombok 程序;

  3. 此时 Lombok 就对第一步得到的 AST 进行处理,找到 @Data 注解所在类对应的语法树(AST),然后修改该语法树,增加 getter 和 setter 方法定义的相应树节点;

  4. javac 使用修改后的抽象语法树(AST)生成字节码文件,即给 class 增加新的节点(代码块)。

这正是 Lombok 与其他注解库最大的不同:它不是「读取注解做点什么」,而是篡改编译器正在处理的语法树。这也是为什么它需要 IDE 插件配合 —— IDE 有自己的编译器,不装插件就无法看到这些凭空多出来的方法。

通过阅读 Lombok 源码可以发现,对应注解的实现都在 HandleXXX 中,比如 @Getter 注解的实现在 HandleGetter.handle()。还有一些其他类库也使用这种方式实现,比如 Google Auto、Dagger 等等。

四、Lombok 的优缺点

优点

  1. 能通过注解的形式自动生成构造器、getter/setter、equals、hashCode、toString 等方法,提高了一定的开发效率;

  2. 让代码变得简洁,不用过多地去关注相应的方法;

  3. 属性做修改时,也简化了维护为这些属性所生成的 getter/setter 方法等。

缺点

  1. 不支持多种参数构造器的重载;

  2. 虽然省去了手动创建 getter/setter 方法的麻烦,但大大降低了源代码的可读性和完整性,降低了阅读源代码的舒适度;

  3. 与 JDK、IDE 版本强绑定,升级 JDK 时常常需要同步升级 Lombok,否则编译直接失败;

  4. 依赖注解处理器,脱离 IDE 用命令行构建、或团队中有人未开启注解处理时,容易出现「我这能编译、你那不能」的差异。

五、常见坑

1. @Data 的 equals/hashCode 不含父类字段

这是最高频的陷阱。子类继承父类后,@Data 生成的 equals 只比较本类字段,两个「父类字段不同」的对象可能被判为相等。需要显式声明:

@Data
@EqualsAndHashCode(callSuper = true)
public class SubUser extends User {
    private String nickname;
}

IDEA 会对此给出警告,不要顺手把它关掉。

2. 实体类与 JPA / MyBatis 的配合

JPA 实体要求有无参构造器,而 @Builder 默认需要全参构造器,两者同时使用时要显式补上:

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Integer id;
    private String username;
}

3. 构造器注入与 Spring 的搭配

@RequiredArgsConstructor 配合 final 字段是 Spring 官方推荐的注入方式,可以省掉 @Autowired,也让依赖一目了然:

@Slf4j
@Service
@RequiredArgsConstructor
public class UserService {

    private final UserMapper userMapper;
    private final RedisTemplate<String, Object> redisTemplate;

    public User getById(Integer id) {
        log.info("query user, id={}", id);
        return userMapper.selectById(id);
    }
}

4. 序列化框架的兼容性

部分序列化框架(如某些版本的 Jackson、Fastjson)依赖 getter 或字段可见性推导属性名。使用 Lombok 后属性名由生成的方法决定,遇到「字段名与 JSON key 不一致」时,优先检查 getter 的命名规则,必要时用 @JsonProperty 显式标注。

5. 版本升级要一步到位

升级 JDK 时如果报出大量「找不到符号」「无法访问 xxx」的错误,先确认 Lombok 版本是否匹配新 JDK,而不是去怀疑业务代码。

六、小结

  1. Lombok 用注解替代样板代码,核心价值是消除 getter/setter/equals/hashCode/toString 的重复劳动。

  2. 使用前提有三:加对依赖(provided 或 optional)、装好 IDE 插件(IDEA 2020.3+ 已内置)、开启注解处理器。

  3. 它的原理是 JSR 269 注解处理器在编译期修改 AST,因此生成的代码只在字节码中存在。

  4. 用之前务必知道 @Data 的 equals/hashCode 不含父类字段这个坑,继承场景要加 @EqualsAndHashCode(callSuper = true)。

  5. 别把版本号写死在文档里照抄 —— Lombok 与 JDK 版本强绑定,新项目交给 Spring Boot BOM 管理即可。

0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区