SpringBoot整合Lombok以及各种使用技巧
1.前言☕
大家好,我是Leo哥🫣🫣🫣,今天给大家带来关于精品SpringBoot专栏,暂且就给他起名为循序渐进学SpringBoot,这里我参考了我上一个专栏:循序渐进学SpringSecurity6。有需要的朋友可以抓紧学习来哈,带你从SpringSecurity从零到实战项目。好了,我们进入正题,为什么会有SpringBoot这个专栏呢,是这样的,今年Leo哥也是正在重塑知识体系,从基础到框架,而SpringBoot又是我们框架中的核心,我觉得很有必要通过以博客的形式将我的知识系列进行输出,同时也锻炼一下自己的写作能力,如果能帮到大家那就更好啦!!!本地系列教程会从SpringBoot基础讲起,会以知识点+实例+项目的学习模式由浅入深对Spring Boot框架进行学习&使用。好了,话不多说让我们开始吧😎😎😎。
2. 什么是Lombok
Lombok 是一款 Java 开发插件,使得 Java 开发者可以通过其定义的一些注解来消除业务工程中冗长和繁琐的代码,尤其对于简单的 Java 模型对象(POJO) 。在开发环境中使用 Lombok 插件后,Java 开发人员可以节省出重复构建,诸如 hashCode 和 equals 这样的方法以及各种业务对象模型的 accessor 和 toString 等方法的大量时间。对于这些方法,Lombok 能够在编译源代码期间自动帮我们生成这些方法,但并不会像反射那样降低程序的性能。
官方简介:
Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more.
简单来说:Lombok是一个Java库,能自动插入编辑器并构建工具,简化Java开发。通过添加注解的方式,不需要为类编写getter或eques方法,同时可以自动化日志变量。
未使用 lombok 的时候,我们还在写着 get、set、toString 方法的冗余代码,这些代码毫无技术含量可言,非常影响美观,于是我们开始使用 lombok
3. 引入Lombok依赖
3.1 Maven
xml
<!-- lombok插件 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<optional>true</optional>
</dependency>
3.2 Gradle
arduino
dependencies {
compileOnly 'org.projectlombok:lombok:1.18.10'
annotationProcessor 'org.projectlombok:lombok:1.18.10'
}
4. 安装Lombok插件(IDEA)
点击 File -> Settings -> Plugins
安装完毕后,重启 IDEA 即可生效。但是现在IDEA基本都自带了Lombok插件,如果没有的小伙伴直接安装下载就行了。
Eclipse、STS 等开发工具需要下载 lombok.jar,这里就不做介绍了。
5. Lombok基本使用
5.1 创建User类(@Data)
arduino
@Data
public class User {
/**
* 主键id
*/
private int id;
/**
* 登录账号
*/
private String name;
/**
* 登录密码
*/
private String password;
/**
* 性别
*/
private int sex;
/**
* 年龄
*/
private int age;
}
该注解自动生成所有属性的getter和setter方法、equals()
和hashCode()
方法,以及一个默认的无参构造函数。
上述代码等价于手写以下内容:
typescript
package org.javatop.lombok.domain;
import java.util.Objects;
/**
* @author : Leo
* @version 1.0
* @date 2024-04-15 22:53
* @description :
*/
public class User {
/**
* 主键id
*/
private int id;
/**
* 登录账号
*/
private String name;
/**
* 登录密码
*/
private String password;
/**
* 性别
*/
private int sex;
/**
* 年龄
*/
private int age;
public User() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.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 id == user.id && sex == user.sex && age == user.age && Objects.equals(name, user.name) && Objects.equals(password, user.password);
}
@Override
public int hashCode() {
return Objects.hash(id, name, password, sex, age);
}
}
5.2 @Builder
@Builder注解可以自动生成Builder模式的代码。Builder模式是一种创建对象的设计模式,它可以让我们更加灵活地创建对象,同时也可以提高代码的可读性和可维护性
arduino
import lombok.Builder;
@Builder
public class User {
private String name;
private int age;
}
5.3 @Slf4j
@Slf4j
用于在 Java 类中自动插入一个静态的 log
属性。这个属性是一个日志记录器(logger),通常与 SLF4J (Simple Logging Facade for Java) 日志门面一起使用。
当你在类上添加了 @Slf4j
注解后,在编译时,Lombok 会自动生成以下代码:
arduino
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SomeClass {
private static final Logger log = LoggerFactory.getLogger(SomeClass.class);
}
这样做的好处是减少了模板代码量,并且提高了可读性和可维护性。
下面是一个使用 @Slf4j
注解的例子:
typescript
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class ExampleService {
public void process(String message) {
if (message == null || message.trim().isEmpty()) {
log.error("Message cannot be null or empty");
throw new IllegalArgumentException("Invalid message");
}
// 处理消息...
log.info("Processing message: {}", message);
}
}
在上述代码示例中,由于我们使用了 @Slf4j
注解,我们可以直接调用 log.error()
和 log.info()
方法来记录错误和信息级别的日志。
5.3 @AllArgsConstructor
@AllArgsConstructor
用于自动生成类的全参构造函数。当你在一个类上使用 @AllArgsConstructor
注解时,Lombok 会为该类创建一个包含所有成员变量作为参数的构造方法。
首先,确保已经将 Lombok 添加到了项目依赖中。
php
package org.javatop.lombok.domain;
import java.util.Objects;
/**
* @author : Leo
* @version 1.0
* @date 2024-04-15 22:53
* @description :
*/
@AllArgsConstructor
public class User {
/**
* 主键id
*/
private int id;
/**
* 登录账号
*/
private String name;
/**
* 登录密码
*/
private String password;
/**
* 性别
*/
private int sex;
/**
* 年龄
*/
private int age;
}
上述代码等同于手动编写如下带有全参构造函数的代码:
arduino
package org.javatop.lombok.domain;
import java.util.Objects;
/**
* @author : Leo
* @version 1.0
* @date 2024-04-15 22:53
* @description :
*/
@AllArgsConstructor
public class User {
/**
* 主键id
*/
private int id;
/**
* 登录账号
*/
private String name;
/**
* 登录密码
*/
private String password;
/**
* 性别
*/
private int sex;
/**
* 年龄
*/
private int age;
public User(int id, String name, String password, int sex, int age) {
this.id = id;
this.name = name;
this.password = password;
this.sex = sex;
this.age = age;
}
}
这样就不需要手动编写重复和繁琐的构造方法代码。这对于减少样板代码、提高开发效率非常有帮助。
以下是一些关于 @AllArgsConstructor
的详细信息:
-
如果某个字段被标记为
final
, 那么它必须被包括在由@AllArgsConstructor
自动生成的构造函数中。 -
如果你不想让某个字段出现在生成的全参数构造函数中,可以使用
@NoArgsConstructor
, 然后手动定义所需参数的构造函数。 -
可以通过给注解添加参数来定制生成的构造方法。例如:
access
: 定义访问级别(例如:AccessLevel.PUBLIC, AccessLevel.PROTECTED)force
: 对 final 字段强制设值为空值(对基本类型来说是默认值)
-
若存在继承关系,则父类属性不会被包含在子类通过 @AllArgsConstructor 自动生成的构造器里。
5.4 @SneakyThrows
@SneakyThrows
是 Lombok 库提供的一个注解,用于简化 Java 异常处理。在 Java 中,当你调用一个声明了抛出受检异常(checked exception)的方法时,你必须要么处理这个异常(通过 try-catch 语句),要么在当前方法上声明抛出该异常。
使用 @SneakyThrows
注解可以避免编写显式的 try-catch 块或者在方法签名中声明抛出异常。Lombok 会自动为你生成相应的代码。这样做使得代码更加简洁,并且减少了模板式代码的冗余。
下面是一个没有使用 @SneakyThrows
的例子:
java
public void readFile(String path) throws IOException {
FileInputStream inputStream = new FileInputStream(path);
// ... 处理文件读取 ...
}
如果我们不想在 readFile
方法上声明抛出 IOException
,通常我们需要写一个 try-catch 块来捕获它:
typescript
public void readFile(String path) {
try {
FileInputStream inputStream = new FileInputStream(path);
// ... 处理文件读取 ...
} catch (IOException e) {
// 处理异常
}
}
而使用 @SneakyThrows
, 我们可以省略掉 catch 或者 throws 声明:
java
import lombok.SneakyThrows;
public class Example {
@SneakyThrows(IOException.class)
public void readFile(String path) {
FileInputStream inputStream = new FileInputStream(path);
// ... 处理文件读取 ...
}
}
Lombok 将会为我们生成包含适当异常处理机制的字节码。实际上,在运行时任何由此方法引发的 IOException
都将被包装成一个未经检查的(unchecked)异常并重新抛出。
需要注意以下几点:
- 使用
@SneakyThrows
可能会隐藏潜在错误和对错误处理流程造成混乱。 - 这种方式可能导致调用者无法清晰地看到哪些受检查的异常可能被抛出。
- 在某些情况下,滥用此注解可能违反编码最佳实践和原则。
- 某些团队或项目可能选择不使用 Lombok 或类似库以保持源代码与字节码之间更直接、可控性更强的关系。
6. Lombok的工作原理
在Lombok使用的过程中,只需要添加相应的注解,无需再为此写任何代码。自动生成的代码到底是如何产生的呢?
核心之处就是对于注解的解析上。JDK5引入了注解的同时,也提供了两种解析方式。
- 运行时解析
运行时能够解析的注解,必须将@Retention设置为RUNTIME,这样就可以通过反射拿到该注解。java.lang.reflect反射包中提供了一个接口AnnotatedElement,该接口定义了获取注解信息的几个方法,Class、Constructor、Field、Method、Package等都实现了该接口,对反射熟悉的朋友应该都会很熟悉这种解析方式。
- 编译时解析
编译时解析有两种机制,分别简单描述下:
- Annotation Processing Tool
apt自JDK5产生,JDK7已标记为过期,不推荐使用,JDK8中已彻底删除,自JDK6开始,可以使用Pluggable Annotation Processing API来替换它,apt被替换主要有2点原因:
- api都在
com.sun.mirror
非标准包下 - 没有集成到javac中,需要额外运行
- Pluggable Annotation Processing API
JSR 269自JDK6加入,作为apt的替代方案,它解决了apt的两个问题,javac在执行的时候会调用实现了该API的程序,这样我们就可以对编译器做一些增强,javac执行的过程如下:
Lombok本质上就是一个实现了 JSR 269 API 的程序。在使用 javac 的过程中,它产生作用的具体流程如下:
- javac对源代码进行分析,生成了一棵抽象语法树(AST)。
- 运行过程中调用实现了 JSR 269 API 的Lombok程序。
- 此时Lombok就对第一步骤得到的AST进行处理,找到@Data注解所在类对应的语法树(AST),然后修改该语法树(AST),增加getter和setter方法定义的相应树节点。
- javac使用修改后的抽象语法树(AST)生成字节码文件,即给class增加新的节点(代码块)。
7. Lombok的优缺点
优点:
- 减少样板代码:Lombok可以自动生成常见的方法,例如构造函数、getters/setters和toString()方法等,从而减少了手动编写这些重复代码的需要。
- 提升可读性:由于减少了冗余代码,使得类文件更加简洁明了。
- 加快开发速度:自动化生成常用方法节省了大量时间,并且能够快速进行原型设计。
- 减轻维护负担:当添加新字段或修改现有字段时无需手动更新相应的getter/setter等方法。
- 支持链式调用风格(Fluent API): 通过
@Accessors(chain = true)
注解可以让setter返回this对象, 这支持链式调用设置属性值。
缺点:
- 不支持多种参数构造器的重载
- 虽然省去了手动创建getter/setter方法的麻烦,但大大降低了源代码的可读性和完整性,降低了阅读源代码的舒适度。
- 工具依赖性强:在没有安装Lombok插件或配置不正确时可能导致IDE无法识别相关语法而报错;此外,构建系统也需要相应地配置才能正常运行。
- 编译器依赖性: Lombok是在编译期间运作, 如果你更换IDE或者编译器, 必须确保它们都支持并正确配置了Lombok。
- 可能影响其他工具: 某些静态分析工具或反射机制依赖源码级别信息, Lombok处理后源码与字节码之间存在差异可能导致问题.
- 版本兼容问题: Java版本更新后,Lombok也必须跟进更新以确保兼容性。
8. Lombok常用注解
-
@Getter/@Setter
:用在属性上,再也不用自己手写setter和getter方法了,还可以指定访问范围 -
@ToString
:用在类上,可以自动覆写toString方法,当然还可以加其他参数,例如@ToString(exclude="id")排除id属性,或者@ToString(callSuper=true, includeFieldNames=true)调用父类的toString方法,包含所有属性 -
@EqualsAndHashCode
:用在类上,自动生成equals方法和hashCode方法 -
@NoArgsConstructor, @RequiredArgsConstructor and @AllArgsConstructor
:用在类上,自动生成无参构造和使用所有参数的构造函数以及把所有@NonNull属性作为参数的构造函数,如果指定staticName = "of"参数,同时还会生成一个返回类对象的静态工厂方法,比使用构造函数方便很多,与@Data
注解连用时,会时@RequiredArgsConstrutor
注解失效 -
@Data
:注解在类上,相当于同时使用了@ToString
、@EqualsAndHashCode
、@Getter
、@Setter
和@RequiredArgsConstrutor
这些注解,对于POJO类
十分有用 -
@Accessors
:用在类上,必须与@Getter/@Setter
连用,用于指定生成的get/set方法的格式,chain = true
属性声明为链式调用,即生成的set方法不返回void而返回this。fluent = true
生成的get/set方法不包含get/set前缀 -
@Value
:用在类上,是@Data的不可变形式,相当于为属性添加final声明,只提供getter方法,而不提供setter方法 -
@Builder
:用在类、构造器、方法上,为你提供复杂的builder APIs,让你可以像如下方式一样调用Person.builder().name("Adam Savage").city("San Francisco").job("Mythbusters").job("Unchained Reaction").build();
更多说明参考Builder -
@SneakyThrows
:自动抛受检异常,而无需显式在方法上使用throws语句 -
@Synchronized
:用在方法上,将方法声明为同步的,并自动加锁,而锁对象是一个私有的属性$lock
或$LOCK
,而java中的synchronized关键字锁对象是this,锁在this或者自己的类对象上存在副作用,就是你不能阻止非受控代码去锁this或者类对象,这可能会导致竞争条件或者其它线程错误 -
@Getter(lazy=true)
:可以替代经典的Double Check Lock样板代码 -
@Log
:根据不同的注解生成不同类型的log对象,但是实例名称都是log,有六种可选实现类@CommonsLog
Creates log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);@Log
Creates log = java.util.logging.Logger.getLogger(LogExample.class.getName());@Log4j
Creates log = org.apache.log4j.Logger.getLogger(LogExample.class);@Log4j2
Creates log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);@Slf4j
Creates log = org.slf4j.LoggerFactory.getLogger(LogExample.class);@XSlf4j
Creates log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);