Lombok+Logback实现日志功能

一、Lombok 详解
Lombok 是一款 Java 开发工具库,核心目标是通过注解简化 Java 类的模板代码(如 getter/setter、构造方法、日志对象等),减少冗余代码,提升开发效率。它的原理是在编译阶段(而非运行时)动态生成字节码,因此不影响程序运行性能。
1. 核心优势
- 消除模板代码:无需手动编写
getXxx()、setXxx()、toString()等重复代码;- 代码更简洁:专注业务逻辑,类结构更清晰;
- 低侵入性:仅在编译期生效,运行时无额外依赖(
scope=provided)。
2. 前置配置(必做)
(1)引入依赖(Maven)
xml
<!-- Lombok 核心依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
(2)IDE 安装插件
- IntelliJ IDEA:Settings → Plugins → 搜索 Lombok → 安装并重启 IDE;
- Eclipse:Marketplace → 搜索 Lombok → 安装,需确保 eclipse.ini 配置 Lombok 路径。
插件作用:让 IDE 识别 Lombok 注解生成的代码,避免 "找不到方法 / 变量" 的语法提示(不装插件不影响编译,但开发体验差)。
安装成功如图

3. Lombok 常用注解
| 注解 | 作用 |
|---|---|
| @Data | 组合注解:包含 @Getter+@Setter+@ToString+@EqualsAndHashCode+@RequiredArgsConstructor |
| @Getter/@Setter | 为字段生成 getter/setter 方法(可加在类上(所有字段)或单个字段上) |
| @ToString | 生成 toString() 方法,可通过 exclude 排除指定字段 |
| @EqualsAndHashCode | 生成 equals() 和 hashCode() 方法 |
| @NoArgsConstructor | 生成无参构造方法 |
| @AllArgsConstructor | 生成全参构造方法 |
| @RequiredArgsConstructor | 为 final 或 @NonNull 字段生成构造方法 |
| @Builder | 实现建造者模式,支持链式调用创建对象 |
| @NonNull | 字段非空校验,赋值为 null 时抛出 NullPointerException |
| @Cleanup | 自动关闭资源(如 IO 流、连接),替代 try-finally |
| @Value | 不可变对象,相当于final类+@Data |
| @SneakyThrows | 隐藏异常捕获(简化try-catch) |
| @Synchronized | 安全的同步锁(替代 synchronized 关键字) |
| @Slf4j | 生成 SLF4J 日志对象 log(本文主要内容) |
4. 示例
1. 基础核心注解(实体类高频)
(1)@Getter/@Setter:生成 getter/setter 方法
java
import lombok.Getter;
import lombok.Setter;
// 类级别:为所有字段生成 getter/setter
@Getter
@Setter
public class GetterSetterDemo {
private Long id;
private String name;
// 字段级别:仅为该字段生成(优先级高于类级别)
@Getter
private Integer age;
public static void main(String[] args) {
GetterSetterDemo demo = new GetterSetterDemo();
demo.setId(1L); // @Setter 生成
demo.setName("张三");// @Setter 生成
System.out.println(demo.getId()); // @Getter 生成
System.out.println(demo.getAge()); // 字段级别 @Getter 生成
}
}
(2)@ToString:生成 toString () 方法
java
import lombok.ToString;
@ToString(
exclude = "age", // 排除指定字段,多个字段使用{"age","name"}
includeFieldNames = true // 是否显示字段名(默认true)
)
public class ToStringDemo {
private Long id;
private String name;
private Integer age;
public static void main(String[] args) {
ToStringDemo demo = new ToStringDemo();
demo.id = 1L;
demo.name = "李四";
demo.age = 20;
// 输出:ToStringDemo(id=1, name=李四)(排除了age)
System.out.println(demo.toString());
}
}
(3)@EqualsAndHashCode:生成 equals ()/hashCode ()
java
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(
of = {"id", "name"}, // 仅基于指定字段生成(避免父类字段干扰)
callSuper = false // 是否包含父类字段(默认false)
)
public class EqualsHashCodeDemo {
private Long id;
private String name;
private Integer age;
public static void main(String[] args) {
EqualsHashCodeDemo d1 = new EqualsHashCodeDemo();
d1.id = 1L;
d1.name = "王五";
d1.age = 20;
EqualsHashCodeDemo d2 = new EqualsHashCodeDemo();
d2.id = 1L;
d2.name = "王五";
d2.age = 30;
// 输出 true(仅比较id和name)
System.out.println(d1.equals(d2));
System.out.println(d1.hashCode() == d2.hashCode()); // true
}
}
(4)@Data:组合注解(Getter+Setter+ToString+EqualsAndHashCode+RequiredArgsConstructor)
java
import lombok.Data;
@Data // 一站式生成核心方法,无需单独加其他注解
public class DataDemo {
private Long id;
private String name;
private final Integer type = 1; // final字段,会被@RequiredArgsConstructor包含
public static void main(String[] args) {
DataDemo demo = new DataDemo(1); // @RequiredArgsConstructor 生成(仅final字段)
demo.setId(100L);
demo.setName("赵六");
// 输出:DataDemo(id=100, name=赵六, type=1)
System.out.println(demo);
}
}
(5)构造方法注解(@NoArgsConstructor/@AllArgsConstructor/@RequiredArgsConstructor)
java
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
@NoArgsConstructor // 无参构造
@AllArgsConstructor // 全参构造
@RequiredArgsConstructor // 为final/@NonNull字段生成构造
public class ConstructorDemo {
private Long id;
@NonNull private String name; // 非空字段
private final Integer status = 0; // final字段
public static void main(String[] args) {
ConstructorDemo d1 = new ConstructorDemo(); // @NoArgsConstructor
ConstructorDemo d2 = new ConstructorDemo(1L, "钱七", 1); // @AllArgsConstructor
ConstructorDemo d3 = new ConstructorDemo("孙八"); // @RequiredArgsConstructor(仅name)
}
}
2. 进阶注解
(1)@Builder:建造者模式(链式创建对象)
java
import lombok.Builder;
import lombok.Data;
@Data
@Builder // 生成建造者方法
public class BuilderDemo {
private Long id;
private String name;
private Integer age;
public static void main(String[] args) {
// 链式调用创建对象,无需手动set
BuilderDemo demo = BuilderDemo.builder()
.id(1L)
.name("周九")
.age(25)
.build();
System.out.println(demo); // BuilderDemo(id=1, name=周九, age=25)
}
}
(2)@NonNull:非空校验
java
import lombok.NonNull;
public class NonNullDemo {
private String name;
public void setName(@NonNull String name) {
this.name = name;
}
public static void main(String[] args) {
NonNullDemo demo = new NonNullDemo();
// 传入null会抛出NullPointerException(Lombok自动生成校验)
demo.setName(null);
}
}
(3)@Cleanup:自动关闭资源
java
import lombok.Cleanup;
import java.io.FileInputStream;
import java.io.IOException;
public class CleanupDemo {
public static void main(String[] args) {
try {
@Cleanup FileInputStream is = new FileInputStream("test.txt");
System.out.println("文件字节数:" + is.available());
} catch (IOException e) {
e.printStackTrace();
} // 自动关闭is
}
}
(4)@Value:不可变对象(替代 final + @Data)
java
import lombok.Value;
@Value // 等价于:final类 + 所有字段final + @Getter + @ToString + @EqualsAndHashCode + @AllArgsConstructor
public class ValueDemo {
Long id;
String name;
Integer age;
public static void main(String[] args) {
ValueDemo demo = new ValueDemo(1L, "吴十", 30);
// demo.setId(2L); // 编译报错:无setter方法(不可变)
System.out.println(demo.getId()); // 仅能读取
}
}
(5)@SneakyThrows:隐藏异常捕获(简化 try-catch)
java
import lombok.SneakyThrows;
import java.io.FileInputStream;
public class SneakyThrowsDemo {
// 自动捕获Checked异常,无需手动try-catch或throws声明
@SneakyThrows
public static void readFile() {
FileInputStream is = new FileInputStream("test.txt");
System.out.println(is.available());
}
public static void main(String[] args) {
readFile(); // 无需处理IOException
}
}
(5)@Synchronized:安全的同步锁(替代 synchronized 关键字)
java
import lombok.Synchronized;
public class SynchronizedDemo {
private final Object lock = new Object();
// 等价于 synchronized(this)
@Synchronized
public void method1() {
System.out.println("方法1同步执行");
}
// 指定锁对象,等价于 synchronized(lock)
@Synchronized("lock")
public void method2() {
System.out.println("方法2同步执行");
}
public static void main(String[] args) {
SynchronizedDemo demo = new SynchronizedDemo();
new Thread(demo::method1).start();
new Thread(demo::method2).start();
}
}
是不是发现少了个@Slf4j ,这里留到最后单独讲解,使用因为这是本文的核心内容
@Slf4j 注解详解
@Slf4j 是 Lombok 框架提供的一个注解,核心作用是自动为类生成日志对象,无需手动编写 private static final Logger log = LoggerFactory.getLogger(XXX.class); 这类重复代码,简化日志开发。
(1)前置条件
1、Slf4j 是 Lombok 框架提供的一个注解,所以需要导入Lombok依赖以及安装插件(已满足)
2、SLF4J + Logback 为例(主流组合),Logback为SpringBoot默认依赖的日志框架所以需要引入SpringBoot依赖,在引入后会自动引入对应版本的依赖

(2)基础使用
java
import lombok.extern.slf4j.Slf4j;
// 注解加在类上,自动生成 log 日志对象
@Slf4j
public class UserService {
public void addUser(String username) {
// 直接使用自动生成的 log 对象打印日志
log.info("开始添加用户,用户名:{}", username); // 信息级日志,支持占位符
try {
// 业务逻辑
log.debug("用户添加中,用户名:{}", username); // 调试级日志(开发环境用)
} catch (Exception e) {
// 错误级日志,传入异常对象,自动打印堆栈信息
log.error("添加用户失败,用户名:{}", username, e);
}
}
public static void main(String[] args) {
new UserService().addUser("张三");
}
}
(3)核心逻辑说明
@Slf4j 会在编译阶段为类生成如下代码(无需手动写):
java
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(UserService.class);
日志级别(从低到高):trace < debug < info < warn < error,可通过配置文件(如 logback.xml)控制输出级别。
占位符 {} 是 SLF4J 的特性,避免手动拼接字符串(如 log.info("用户名:" + username)),提升性能和可读性。
(4)日志输入格式
Spring Boot 默认使用 Logback 作为日志框架,因此配置主要围绕 Logback 展开。日志格式的核心要素包括:
时间戳(%d)
日志级别(%level)
线程名(%thread)
类 / 方法名(%logger/%M)
日志内容(%msg)
换行(%n)
方案 1:通过 application.yml/properties 快速修改(简单场景)
适合只需要微调默认格式的场景,无需编写 XML 配置,直接在 Spring Boot 核心配置文件中设置。
示例:application.yml 配置
yml
# application.yml
logging:
# 全局日志级别(可针对特定包单独设置)
level:
root: INFO # 根日志级别
com.example.demo: DEBUG # 自定义包的日志级别
# 控制台输出格式
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"
# 文件输出格式(如果开启了文件日志)
file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"
# 可选:开启日志文件输出(指定路径和大小)
file:
name: ./logs/demo.log # 日志文件路径
logback:
rollingpolicy:
max-file-size: 10MB # 单个日志文件最大大小
max-history: 7 # 日志文件保留天数
格式参数解释
| 参数 | 含义 |
|---|---|
| %d{yyyy-MM-dd HH:mm:ss.SSS} | 时间戳,精确到毫秒 |
| [%thread] | 线程名(方括号是自定义格式,可去掉) |
| %-5level | 日志级别(-5 表示左对齐,占 5 个字符,如 INFO、DEBUG) |
| %logger{50} | 类名 / Logger 名称({50} 表示最长显示 50 个字符,超出会截断) |
| %msg | 日志内容(业务代码中 log.info("xxx") 里的内容) |
| %n | 换行符(保证每条日志单独一行) |
方案 2:通过 logback-spring.xml 自定义(复杂场景)
如果需要更精细的控制(比如不同日志级别用不同颜色、分模块输出、自定义过滤器),推荐使用 Logback 专属配置文件。
步骤 1:创建配置文件
在 src/main/resources 下新建 logback-spring.xml(Spring Boot 会自动识别这个文件名)。
步骤 2:完整配置示例(含彩色输出 + 多文件拆分)
xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
<!-- 基础配置:定义变量(方便复用) -->
<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/>
<property name="LOG_PATH" value="./logs"/> <!-- 日志文件根路径 -->
<!-- 1. 控制台输出 Appender(支持彩色) -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!-- 开启彩色输出(仅控制台生效) -->
<withJansi>true</withJansi>
<!-- 日志格式(复用上面定义的变量) -->
<pattern>${LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 2. 文件输出 Appender(滚动日志,避免单个文件过大) -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 日志文件路径 -->
<file>${LOG_PATH}/demo.log</file>
<!-- 滚动策略:按大小+日期拆分 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 拆分后的日志文件名:demo-2024-03-13.log -->
<fileNamePattern>${LOG_PATH}/demo-%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 单个文件最大大小 -->
<maxFileSize>10MB</maxFileSize>
<!-- 日志保留天数 -->
<maxHistory>7</maxHistory>
<!-- 总日志大小上限(超出会删除旧文件) -->
<totalSizeCap>100MB</totalSizeCap>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 3. 针对特定包的日志配置(比如DAO层单独输出) -->
<logger name="com.example.demo.mapper" level="DEBUG" additivity="false">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</logger>
<!-- 4. 根日志配置 -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
</configuration>
关键配置解释
彩色输出:true 开启后,控制台日志会按级别显示不同颜色(INFO 绿色、ERROR 红色、WARN 黄色),需确保 IDE 控制台支持 ANSI 颜色(IDEA 默认支持)。
additivity="false":避免特定包的日志被重复输出到根日志(比如 mapper 层的日志只输出一次)。
滚动策略:TimeBasedRollingPolicy 实现「按日期 + 大小」拆分日志,防止日志文件过大。