第一部分:Lombok 概述与核心概念
1.1 什么是 Lombok?
Lombok是一个 Java 库,通过注解的方式自动生成 Java 代码,主要目标是减少 Java 代码中的样板代码(boilerplate code),提高开发效率和代码的可读性。
1.1.1 传统 Java 代码的问题
java
// 传统 Java Bean public class User { private Long id; private String name; private String email; private int age; // 构造方法 public User() {} public User(Long id, String name, String email, int age) { this.id = id; this.name = name; this.email = email; this.age = age; } // Getter 和 Setter 方法 public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } // ... 更多 getter/setter // equals 和 hashCode 方法 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; return age == user.age && Objects.equals(id, user.id) && Objects.equals(name, user.name) && Objects.equals(email, user.email); } @Override public int hashCode() { return Objects.hash(id, name, email, age); } // toString 方法 @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", email='" + email + '\'' + ", age=" + age + '}'; } }1.1.2 使用 Lombok 后的代码
java
// 使用 Lombok 的 Java Bean @Data @NoArgsConstructor @AllArgsConstructor @Builder public class User { private Long id; private String name; private String email; private int age; }对比分析:
传统方式:60+ 行代码
Lombok 方式:仅需 8 行代码
代码量减少约87%
1.2 Lombok 的核心价值
1.2.1 主要优势
减少样板代码:自动生成 getter、setter、equals、hashCode、toString 等方法
提高开发效率:减少重复代码编写时间
提高代码可读性:类定义更加简洁,聚焦业务逻辑
降低维护成本:自动生成的方法始终保持一致性
编译时生成:不影响运行时性能
1.2.2 工作原理
Lombok 使用Java 注解处理器(Annotation Processor)在编译时修改抽象语法树(AST),从而生成新的 Java 字节码。这意味着:
不是运行时反射:不影响程序性能
编译时完成:生成的代码在编译阶段就已经存在
IDE 友好:配合插件可以在 IDE 中直接使用
第二部分:Lombok 环境安装与配置
2.1 系统要求
2.1.1 基本要求
Java 版本:Java 8 或更高版本
构建工具:Maven、Gradle 或 Ant
IDE 支持:IntelliJ IDEA、Eclipse、NetBeans、VS Code 等
2.1.2 版本兼容性
| Lombok 版本 | 最低 Java 版本 | 主要特性 |
|---|---|---|
| 1.18.x | Java 8+ | 当前主流稳定版本 |
| 1.16.x | Java 6+ | 兼容旧版本 |
| Edge 版本 | Java 11+ | 实验性功能 |
2.2 IDE 插件安装
2.2.1 IntelliJ IDEA 安装
方式一:通过插件市场安装(推荐)
打开 IntelliJ IDEA
进入
File → Settings → Plugins搜索 "Lombok"
点击 "Install"
重启 IDEA
方式二:手动安装
下载 lombok.jar
命令行执行:
java -jar lombok.jar在弹出的窗口中选择 IDEA 安装路径
点击 "Install / Update"
重启 IDEA
配置 Lombok 注解处理器:
进入
File → Settings → Build, Execution, Deployment → Compiler → Annotation Processors勾选 "Enable annotation processing"
2.2.2 Eclipse 安装
方式一:使用安装程序
bash
# 下载 lombok.jar wget https://projectlombok.org/downloads/lombok.jar # 运行安装程序 java -jar lombok.jar
方式二:手动配置
将 lombok.jar 复制到 Eclipse 安装目录
编辑
eclipse.ini,在末尾添加:
text
-javaagent:lombok.jar -Xbootclasspath/a:lombok.jar
重启 Eclipse
2.2.3 Visual Studio Code 安装
打开 VS Code
进入扩展市场(Extensions)
搜索 "Lombok Annotations Support"
安装扩展
安装 "Language Support for Java(TM) by Red Hat"
2.3 Maven 项目配置
2.3.1 基础配置
xml
<!-- pom.xml --> <project> <dependencies> <!-- Lombok 依赖 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.30</version> <!-- 使用最新版本 --> <scope>provided</scope> </dependency> </dependencies> </project>
2.3.2 完整 Maven 配置示例
xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>lombok-demo</artifactId> <version>1.0.0</version> <properties> <java.version>11</java.version> <maven.compiler.source>${java.version}</maven.compiler.source> <maven.compiler.target>${java.version}</maven.compiler.target> <lombok.version>1.18.30</lombok.version> </properties> <dependencies> <!-- Lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> <scope>provided</scope> </dependency> <!-- 测试依赖 --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.9.2</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <!-- 编译器插件 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <source>${java.version}</source> <target>${java.version}</target> <annotationProcessorPaths> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> </path> </annotationProcessorPaths> </configuration> </plugin> </plugins> </build> </project>2.3.3 多模块项目配置
xml
<!-- 父模块 pom.xml --> <project> <dependencyManagement> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.30</version> <scope>provided</scope> </dependency> </dependencies> </dependencyManagement> <build> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <annotationProcessorPaths> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.30</version> </path> </annotationProcessorPaths> </configuration> </plugin> </plugins> </pluginManagement> </build> </project> <!-- 子模块 pom.xml --> <project> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> </project>
2.4 Gradle 项目配置
2.4.1 基础配置
gradle
// build.gradle plugins { id 'java' } repositories { mavenCentral() } dependencies { // Lombok 依赖 compileOnly 'org.projectlombok:lombok:1.18.30' annotationProcessor 'org.projectlombok:lombok:1.18.30' // 测试依赖 testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2' testCompileOnly 'org.projectlombok:lombok:1.18.30' testAnnotationProcessor 'org.projectlombok:lombok:1.18.30' } java { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 }2.4.2 使用 Gradle Kotlin DSL
kotlin
// build.gradle.kts plugins { java } repositories { mavenCentral() } dependencies { // Lombok compileOnly("org.projectlombok:lombok:1.18.30") annotationProcessor("org.projectlombok:lombok:1.18.30") // 测试 testImplementation("org.junit.jupiter:junit-jupiter:5.9.2") testCompileOnly("org.projectlombok:lombok:1.18.30") testAnnotationProcessor("org.projectlombok:lombok:1.18.30") } java { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 }2.4.3 Gradle 多项目配置
gradle
// settings.gradle rootProject.name = 'lombok-multimodule' include 'module-core' include 'module-web' include 'module-service' // build.gradle (根项目) subprojects { apply plugin: 'java' repositories { mavenCentral() } dependencies { compileOnly 'org.projectlombok:lombok:1.18.30' annotationProcessor 'org.projectlombok:lombok:1.18.30' } java { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 } }2.5 验证安装
2.5.1 创建测试类
java
// User.java import lombok.Data; import lombok.AllArgsConstructor; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class User { private Long id; private String name; private String email; } // Main.java public class Main { public static void main(String[] args) { User user = new User(1L, "张三", "zhangsan@example.com"); System.out.println("User: " + user); System.out.println("Name: " + user.getName()); user.setEmail("newemail@example.com"); System.out.println("Updated email: " + user.getEmail()); } }2.5.2 编译和运行
bash
# Maven mvn clean compile mvn exec:java -Dexec.mainClass="com.example.Main" # Gradle gradle clean build gradle run
2.5.3 检查生成的字节码
bash
# 查看生成的字节码 javap -c User.class # 或使用反编译工具查看 javap -p User.class
第三部分:Spring Boot 集成 Lombok
3.1 Spring Boot 项目初始化
3.1.1 使用 Spring Initializr
访问 start.spring.io 或使用 IDE 创建 Spring Boot 项目时选择 Lombok 依赖。
Maven 依赖选择:
xml
<dependencies> <!-- Spring Boot Starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!-- Spring Boot Web Starter (可选) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring Boot Data JPA (可选) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!-- Lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!-- 测试 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
3.1.2 完整的 Spring Boot + Lombok 配置
xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <!-- Spring Boot 父依赖 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.1.5</version> <relativePath/> </parent> <groupId>com.example</groupId> <artifactId>spring-boot-lombok-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spring-boot-lombok-demo</name> <description>Spring Boot with Lombok Demo</description> <properties> <java.version>17</java.version> <lombok.version>1.18.30</lombok.version> </properties> <dependencies> <!-- Spring Boot Starters --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!-- 数据库驱动 (示例使用 H2) --> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <!-- Lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> <optional>true</optional> </dependency> <!-- 验证 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <!-- 测试 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <annotationProcessorPaths> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> </path> </annotationProcessorPaths> </configuration> </plugin> </plugins> </build> </project>3.2 Spring Boot 实体类 Lombok 应用
3.2.1 JPA 实体类
java
package com.example.demo.entity; import jakarta.persistence.*; import lombok.*; import java.time.LocalDateTime; @Entity @Table(name = "users") @Data // 生成 getter, setter, equals, hashCode, toString @NoArgsConstructor // 生成无参构造器 @AllArgsConstructor // 生成全参构造器 @Builder // 生成构建器模式 @EntityListeners(AuditingEntityListener.class) // JPA 审计 public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false, unique = true) private String username; @Column(nullable = false) private String password; @Column(nullable = false, unique = true) private String email; private String nickname; private Integer age; @Enumerated(EnumType.STRING) @Column(nullable = false) private UserStatus status = UserStatus.ACTIVE; @CreatedDate @Column(nullable = false, updatable = false) private LocalDateTime createTime; @LastModifiedDate @Column(nullable = false) private LocalDateTime updateTime; // 枚举定义 public enum UserStatus { ACTIVE, INACTIVE, LOCKED } }3.2.2 DTO(数据传输对象)
java
package com.example.demo.dto; import jakarta.validation.constraints.*; import lombok.*; import java.time.LocalDateTime; @Data @Builder @NoArgsConstructor @AllArgsConstructor public class UserDTO { @Null(groups = Create.class) // 创建时自动生成 @NotNull(groups = Update.class) // 更新时必须提供 private Long id; @NotBlank(groups = {Create.class, Update.class}) @Size(min = 3, max = 50) private String username; @NotBlank(groups = Create.class) @Size(min = 6, max = 100) private String password; @NotBlank(groups = {Create.class, Update.class}) @Email private String email; @Size(max = 50) private String nickname; @Min(0) @Max(150) private Integer age; private UserStatus status; // 验证分组 public interface Create {} public interface Update {} public enum UserStatus { ACTIVE, INACTIVE, LOCKED } }3.2.3 VO(视图对象)
java
package com.example.demo.vo; import lombok.*; import java.time.LocalDateTime; @Data @Builder @NoArgsConstructor @AllArgsConstructor public class UserVO { private Long id; private String username; private String email; private String nickname; private Integer age; private String status; private LocalDateTime createTime; private LocalDateTime updateTime; }3.3 Spring Boot 配置类 Lombok 应用
3.3.1 配置属性类
java
package com.example.demo.config; import lombok.*; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import java.time.Duration; import java.util.List; @Component @ConfigurationProperties(prefix = "app") @Data @NoArgsConstructor public class ApplicationProperties { // 应用信息 private AppInfo info = new AppInfo(); // 安全配置 private Security security = new Security(); // 缓存配置 private Cache cache = new Cache(); // 数据库配置 private Database database = new Database(); // 外部服务 private ExternalServices externalServices = new ExternalServices(); // 嵌套配置类 @Data @NoArgsConstructor public static class AppInfo { private String name = "Demo Application"; private String version = "1.0.0"; private String description; } @Data @NoArgsConstructor public static class Security { private String secretKey; private Duration tokenExpiration = Duration.ofHours(2); private List<String> allowedOrigins = List.of("*"); private boolean enableCsrf = true; } @Data @NoArgsConstructor public static class Cache { private Duration defaultTtl = Duration.ofMinutes(30); private String redisHost = "localhost"; private int redisPort = 6379; } @Data @NoArgsConstructor public static class Database { private int connectionTimeout = 30000; private int maximumPoolSize = 10; private boolean showSql = false; } @Data @NoArgsConstructor public static class ExternalServices { private PaymentService payment = new PaymentService(); private NotificationService notification = new NotificationService(); @Data @NoArgsConstructor public static class PaymentService { private String url = "https://api.payment.example.com"; private String apiKey; private int timeout = 5000; } @Data @NoArgsConstructor public static class NotificationService { private String url = "https://api.notification.example.com"; private String apiKey; private boolean enabled = true; } } }3.3.2 配置文件
yaml
# application.yml app: info: name: Spring Boot Lombok Demo version: 2.0.0 description: 演示项目 - Spring Boot 集成 Lombok security: secret-key: my-secret-key-123456 token-expiration: 7200s # 2小时 allowed-origins: - http://localhost:8080 - http://localhost:3000 enable-csrf: true cache: default-ttl: 1800s # 30分钟 redis-host: localhost redis-port: 6379 database: connection-timeout: 30000 maximum-pool-size: 20 show-sql: true external-services: payment: url: https://api.payment.example.com/v1 api-key: ${PAYMENT_API_KEY:default-key} timeout: 10000 notification: url: https://api.notification.example.com/v1 api-key: ${NOTIFICATION_API_KEY:default-key} enabled: true spring: datasource: url: jdbc:h2:mem:testdb driver-class-name: org.h2.Driver username: sa password: jpa: hibernate: ddl-auto: update show-sql: true properties: hibernate: format_sql: true h2: console: enabled: true path: /h2-console server: port: 80803.4 Spring Boot 服务层 Lombok 应用
3.4.1 Service 接口和实现
java
package com.example.demo.service; import com.example.demo.dto.UserDTO; import com.example.demo.vo.UserVO; import java.util.List; public interface UserService { UserVO createUser(UserDTO userDTO); UserVO updateUser(Long id, UserDTO userDTO); UserVO getUserById(Long id); List<UserVO> getAllUsers(int page, int size); void deleteUser(Long id); UserVO changeStatus(Long id, String status); }java
package com.example.demo.service.impl; import com.example.demo.dto.UserDTO; import com.example.demo.entity.User; import com.example.demo.exception.ResourceNotFoundException; import com.example.demo.mapper.UserMapper; import com.example.demo.repository.UserRepository; import com.example.demo.service.UserService; import com.example.demo.vo.UserVO; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.stream.Collectors; @Service @RequiredArgsConstructor // 生成构造器注入 @Slf4j // 生成日志对象 @Transactional // 事务管理 public class UserServiceImpl implements UserService { private final UserRepository userRepository; private final UserMapper userMapper; @Override @Transactional @CacheEvict(value = "users", allEntries = true) public UserVO createUser(UserDTO userDTO) { log.info("Creating user with username: {}", userDTO.getUsername()); // 检查用户名是否已存在 if (userRepository.existsByUsername(userDTO.getUsername())) { throw new IllegalArgumentException("用户名已存在"); } // 检查邮箱是否已存在 if (userRepository.existsByEmail(userDTO.getEmail())) { throw new IllegalArgumentException("邮箱已存在"); } User user = userMapper.toEntity(userDTO); User savedUser = userRepository.save(user); log.debug("User created successfully: {}", savedUser.getId()); return userMapper.toVO(savedUser); } @Override @Transactional @CacheEvict(value = "users", allEntries = true) public UserVO updateUser(Long id, UserDTO userDTO) { log.info("Updating user with id: {}", id); User existingUser = userRepository.findById(id) .orElseThrow(() -> new ResourceNotFoundException("用户不存在")); // 更新用户信息 userMapper.updateEntity(userDTO, existingUser); User updatedUser = userRepository.save(existingUser); log.debug("User updated successfully: {}", id); return userMapper.toVO(updatedUser); } @Override @Transactional(readOnly = true) @Cacheable(value = "users", key = "#id") public UserVO getUserById(Long id) { log.debug("Fetching user by id: {}", id); User user = userRepository.findById(id) .orElseThrow(() -> new ResourceNotFoundException("用户不存在")); return userMapper.toVO(user); } @Override @Transactional(readOnly = true) @Cacheable(value = "users", key = "'page_' + #page + '_size_' + #size") public List<UserVO> getAllUsers(int page, int size) { log.debug("Fetching all users - page: {}, size: {}", page, size); PageRequest pageRequest = PageRequest.of( page, size, Sort.by(Sort.Direction.DESC, "createTime") ); Page<User> userPage = userRepository.findAll(pageRequest); return userPage.getContent().stream() .map(userMapper::toVO) .collect(Collectors.toList()); } @Override @Transactional @CacheEvict(value = "users", allEntries = true) public void deleteUser(Long id) { log.info("Deleting user with id: {}", id); if (!userRepository.existsById(id)) { throw new ResourceNotFoundException("用户不存在"); } userRepository.deleteById(id); log.debug("User deleted successfully: {}", id); } @Override @Transactional @CacheEvict(value = "users", allEntries = true) public UserVO changeStatus(Long id, String status) { log.info("Changing status for user {} to {}", id, status); User user = userRepository.findById(id) .orElseThrow(() -> new ResourceNotFoundException("用户不存在")); User.UserStatus newStatus; try { newStatus = User.UserStatus.valueOf(status.toUpperCase()); } catch (IllegalArgumentException e) { throw new IllegalArgumentException("无效的状态值: " + status); } user.setStatus(newStatus); User updatedUser = userRepository.save(user); return userMapper.toVO(updatedUser); } }3.4.2 异常类
java
package com.example.demo.exception; import lombok.Getter; import org.springframework.http.HttpStatus; @Getter public class ResourceNotFoundException extends RuntimeException { private final String resourceName; private final String fieldName; private final Object fieldValue; public ResourceNotFoundException(String message) { super(message); this.resourceName = null; this.fieldName = null; this.fieldValue = null; } public ResourceNotFoundException(String resourceName, String fieldName, Object fieldValue) { super(String.format("%s not found with %s : '%s'", resourceName, fieldName, fieldValue)); this.resourceName = resourceName; this.fieldName = fieldName; this.fieldValue = fieldValue; } } @Getter public class BusinessException extends RuntimeException { private final String errorCode; private final HttpStatus httpStatus; public BusinessException(String message) { super(message); this.errorCode = "BUSINESS_ERROR"; this.httpStatus = HttpStatus.BAD_REQUEST; } public BusinessException(String message, String errorCode, HttpStatus httpStatus) { super(message); this.errorCode = errorCode; this.httpStatus = httpStatus; } }3.5 Spring Boot 控制器层 Lombok 应用
3.5.1 REST 控制器
java
package com.example.demo.controller; import com.example.demo.dto.UserDTO; import com.example.demo.service.UserService; import com.example.demo.vo.ApiResponse; import com.example.demo.vo.UserVO; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/api/v1/users") @RequiredArgsConstructor @Slf4j @Validated @Tag(name = "用户管理", description = "用户管理相关API") public class UserController { private final UserService userService; @PostMapping @Operation(summary = "创建用户", description = "创建一个新用户") public ResponseEntity<ApiResponse<UserVO>> createUser( @Valid @RequestBody UserDTO userDTO) { log.info("Received request to create user: {}", userDTO.getUsername()); UserVO userVO = userService.createUser(userDTO); ApiResponse<UserVO> response = ApiResponse.<UserVO>builder() .success(true) .code(HttpStatus.CREATED.value()) .message("用户创建成功") .data(userVO) .build(); return ResponseEntity.status(HttpStatus.CREATED).body(response); } @GetMapping("/{id}") @Operation(summary = "获取用户", description = "根据ID获取用户信息") public ResponseEntity<ApiResponse<UserVO>> getUserById( @PathVariable @Parameter(description = "用户ID") Long id) { log.debug("Fetching user with id: {}", id); UserVO userVO = userService.getUserById(id); ApiResponse<UserVO> response = ApiResponse.<UserVO>builder() .success(true) .code(HttpStatus.OK.value()) .message("用户获取成功") .data(userVO) .build(); return ResponseEntity.ok(response); } @GetMapping @Operation(summary = "获取用户列表", description = "分页获取用户列表") public ResponseEntity<ApiResponse<List<UserVO>>> getAllUsers( @RequestParam(defaultValue = "0") @Parameter(description = "页码,从0开始") int page, @RequestParam(defaultValue = "10") @Parameter(description = "每页大小") int size) { log.debug("Fetching users - page: {}, size: {}", page, size); List<UserVO> users = userService.getAllUsers(page, size); ApiResponse<List<UserVO>> response = ApiResponse.<List<UserVO>>builder() .success(true) .code(HttpStatus.OK.value()) .message("用户列表获取成功") .data(users) .build(); return ResponseEntity.ok(response); } @PutMapping("/{id}") @Operation(summary = "更新用户", description = "更新用户信息") public ResponseEntity<ApiResponse<UserVO>> updateUser( @PathVariable Long id, @Valid @RequestBody UserDTO userDTO) { log.info("Updating user with id: {}", id); UserVO userVO = userService.updateUser(id, userDTO); ApiResponse<UserVO> response = ApiResponse.<UserVO>builder() .success(true) .code(HttpStatus.OK.value()) .message("用户更新成功") .data(userVO) .build(); return ResponseEntity.ok(response); } @DeleteMapping("/{id}") @Operation(summary = "删除用户", description = "删除指定用户") public ResponseEntity<ApiResponse<Void>> deleteUser( @PathVariable Long id) { log.info("Deleting user with id: {}", id); userService.deleteUser(id); ApiResponse<Void> response = ApiResponse.<Void>builder() .success(true) .code(HttpStatus.OK.value()) .message("用户删除成功") .build(); return ResponseEntity.ok(response); } @PatchMapping("/{id}/status") @Operation(summary = "修改用户状态", description = "修改用户状态") public ResponseEntity<ApiResponse<UserVO>> changeStatus( @PathVariable Long id, @RequestParam String status) { log.info("Changing status for user {} to {}", id, status); UserVO userVO = userService.changeStatus(id, status); ApiResponse<UserVO> response = ApiResponse.<UserVO>builder() .success(true) .code(HttpStatus.OK.value()) .message("用户状态修改成功") .data(userVO) .build(); return ResponseEntity.ok(response); } }3.5.2 API 响应对象
java
package com.example.demo.vo; import lombok.*; import org.springframework.http.HttpStatus; import java.time.LocalDateTime; @Data @Builder @NoArgsConstructor @AllArgsConstructor public class ApiResponse<T> { private boolean success; private int code; private String message; private T data; private LocalDateTime timestamp; private String path; private Object metadata; // 成功响应 - 无数据 public static ApiResponse<Void> success() { return ApiResponse.<Void>builder() .success(true) .code(HttpStatus.OK.value()) .message("操作成功") .timestamp(LocalDateTime.now()) .build(); } // 成功响应 - 有数据 public static <T> ApiResponse<T> success(T data) { return ApiResponse.<T>builder() .success(true) .code(HttpStatus.OK.value()) .message("操作成功") .data(data) .timestamp(LocalDateTime.now()) .build(); } // 成功响应 - 自定义消息 public static <T> ApiResponse<T> success(T data, String message) { return ApiResponse.<T>builder() .success(true) .code(HttpStatus.OK.value()) .message(message) .data(data) .timestamp(LocalDateTime.now()) .build(); } // 错误响应 public static ApiResponse<Void> error(String message) { return ApiResponse.<Void>builder() .success(false) .code(HttpStatus.BAD_REQUEST.value()) .message(message) .timestamp(LocalDateTime.now()) .build(); } // 错误响应 - 自定义状态码 public static ApiResponse<Void> error(int code, String message) { return ApiResponse.<Void>builder() .success(false) .code(code) .message(message) .timestamp(LocalDateTime.now()) .build(); } // 分页响应 @Data @Builder @NoArgsConstructor @AllArgsConstructor public static class PageInfo { private int page; private int size; private long totalElements; private int totalPages; private boolean hasNext; private boolean hasPrevious; } }3.6 Spring Boot 配置类
3.6.1 全局异常处理
java
package com.example.demo.config; import com.example.demo.exception.BusinessException; import com.example.demo.exception.ResourceNotFoundException; import com.example.demo.vo.ApiResponse; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.ConstraintViolationException; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.context.request.WebRequest; import java.time.LocalDateTime; import java.util.HashMap; import java.util.Map; @Slf4j @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(ResourceNotFoundException.class) public ResponseEntity<ApiResponse<Void>> handleResourceNotFoundException( ResourceNotFoundException ex, HttpServletRequest request) { log.error("Resource not found: {}", ex.getMessage()); ApiResponse<Void> response = ApiResponse.<Void>builder() .success(false) .code(HttpStatus.NOT_FOUND.value()) .message(ex.getMessage()) .timestamp(LocalDateTime.now()) .path(request.getRequestURI()) .build(); return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response); } @ExceptionHandler(BusinessException.class) public ResponseEntity<ApiResponse<Void>> handleBusinessException( BusinessException ex, HttpServletRequest request) { log.error("Business exception: {}", ex.getMessage()); ApiResponse<Void> response = ApiResponse.<Void>builder() .success(false) .code(ex.getHttpStatus().value()) .message(ex.getMessage()) .timestamp(LocalDateTime.now()) .path(request.getRequestURI()) .build(); return ResponseEntity.status(ex.getHttpStatus()).body(response); } @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<ApiResponse<Map<String, String>>> handleValidationExceptions( MethodArgumentNotValidException ex, HttpServletRequest request) { log.error("Validation error: {}", ex.getMessage()); Map<String, String> errors = new HashMap<>(); ex.getBindingResult().getAllErrors().forEach((error) -> { String fieldName = ((FieldError) error).getField(); String errorMessage = error.getDefaultMessage(); errors.put(fieldName, errorMessage); }); ApiResponse<Map<String, String>> response = ApiResponse.<Map<String, String>>builder() .success(false) .code(HttpStatus.BAD_REQUEST.value()) .message("参数验证失败") .data(errors) .timestamp(LocalDateTime.now()) .path(request.getRequestURI()) .build(); return ResponseEntity.badRequest().body(response); } @ExceptionHandler(ConstraintViolationException.class) public ResponseEntity<ApiResponse<Map<String, String>>> handleConstraintViolationException( ConstraintViolationException ex, HttpServletRequest request) { log.error("Constraint violation: {}", ex.getMessage()); Map<String, String> errors = new HashMap<>(); ex.getConstraintViolations().forEach(violation -> { String fieldName = violation.getPropertyPath().toString(); String errorMessage = violation.getMessage(); errors.put(fieldName, errorMessage); }); ApiResponse<Map<String, String>> response = ApiResponse.<Map<String, String>>builder() .success(false) .code(HttpStatus.BAD_REQUEST.value()) .message("参数验证失败") .data(errors) .timestamp(LocalDateTime.now()) .path(request.getRequestURI()) .build(); return ResponseEntity.badRequest().body(response); } @ExceptionHandler(Exception.class) public ResponseEntity<ApiResponse<Void>> handleGlobalException( Exception ex, HttpServletRequest request) { log.error("Unexpected error: {}", ex.getMessage(), ex); ApiResponse<Void> response = ApiResponse.<Void>builder() .success(false) .code(HttpStatus.INTERNAL_SERVER_ERROR.value()) .message("服务器内部错误") .timestamp(LocalDateTime.now()) .path(request.getRequestURI()) .build(); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); } }3.6.2 Web 配置
java
package com.example.demo.config; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; import org.springframework.format.FormatterRegistry; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @RequiredArgsConstructor public class WebConfig implements WebMvcConfigurer { private final ApplicationProperties applicationProperties; @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOrigins(applicationProperties.getSecurity().getAllowedOrigins().toArray(new String[0])) .allowedMethods("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } @Override public void addFormatters(FormatterRegistry registry) { // 添加自定义格式化器 } @Override public void addInterceptors(InterceptorRegistry registry) { // 添加拦截器 } }第四部分:Lombok 高级特性详解
4.1 构造函数相关注解
4.1.1 @NoArgsConstructor
java
import lombok.NoArgsConstructor; @NoArgsConstructor public class User { private Long id; private String name; // Lombok 生成: // public User() {} }参数配置:
java
@NoArgsConstructor( access = AccessLevel.PROTECTED, // 访问级别 force = true // final 字段初始化为默认值 ) public class ImmutableUser { private final Long id; // force=true 时初始化为 null private final String name; // force=true 时初始化为 null // 生成:protected ImmutableUser() {} }4.1.2 @AllArgsConstructor
java
import lombok.AllArgsConstructor; @AllArgsConstructor public class User { private Long id; private String name; private String email; // Lombok 生成: // public User(Long id, String name, String email) { // this.id = id; // this.name = name; // this.email = email; // } }参数配置:
java
@AllArgsConstructor( access = AccessLevel.PACKAGE, // 包级访问 staticName = "create" // 静态工厂方法 ) public class User { private Long id; private String name; // 生成: // static User create(Long id, String name) { // return new User(id, name); // } // private User(Long id, String name) { ... } }4.1.3 @RequiredArgsConstructor
java
import lombok.RequiredArgsConstructor; @RequiredArgsConstructor public class UserService { private final UserRepository userRepository; private final EmailService emailService; private String serviceName = "DefaultService"; // Lombok 生成: // public UserService(UserRepository userRepository, EmailService emailService) { // this.userRepository = userRepository; // this.emailService = emailService; // } }@NonNull 配合使用:
java
import lombok.NonNull; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor public class OrderService { @NonNull private final OrderRepository orderRepository; @NonNull private final PaymentService paymentService; private final AuditService auditService; // 可选的 // Lombok 生成: // public OrderService(@NonNull OrderRepository orderRepository, // @NonNull PaymentService paymentService) { // if (orderRepository == null) throw new NullPointerException("orderRepository"); // if (paymentService == null) throw new NullPointerException("paymentService"); // this.orderRepository = orderRepository; // this.paymentService = paymentService; // } }4.2 Getter/Setter 相关注解
4.2.1 访问级别控制
java
import lombok.Getter; import lombok.Setter; import lombok.AccessLevel; public class User { // 不同访问级别的 Getter/Setter @Getter(AccessLevel.PUBLIC) @Setter(AccessLevel.PRIVATE) private Long id; @Getter @Setter private String name; @Getter(AccessLevel.PROTECTED) private String email; @Setter(AccessLevel.NONE) // 不生成 setter private final String username; // 类级别控制 @Getter @Setter public static class Address { private String street; private String city; private String zipCode; } }4.2.2 延迟初始化
java
import lombok.Getter; public class CacheService { @Getter(lazy = true) private final Map<String, Object> cache = initCache(); private Map<String, Object> initCache() { System.out.println("Initializing cache..."); Map<String, Object> map = new HashMap<>(); // 复杂的初始化逻辑 return map; } // 使用: // CacheService service = new CacheService(); // service.getCache(); // 第一次调用时初始化 }4.3 @Builder 高级用法
4.3.1 基础 Builder 模式
java
import lombok.Builder; import lombok.Singular; import java.util.List; @Builder public class Product { private Long id; private String name; private String description; private Double price; @Singular private List<String> tags; @Builder.Default private Integer stock = 0; @Builder.Default private Boolean active = true; } // 使用示例: Product product = Product.builder() .id(1L) .name("Laptop") .description("高性能笔记本电脑") .price(5999.99) .tag("电子产品") .tag("电脑") .tag("办公") .stock(100) .build();4.3.2 @Builder 在方法级别
java
import lombok.Builder; public class Order { private Long id; private String orderNumber; private List<OrderItem> items; @Builder(builderMethodName = "newOrder", buildMethodName = "create") public static Order createOrder(Long id, String orderNumber, List<OrderItem> items) { Order order = new Order(); order.id = id; order.orderNumber = orderNumber; order.items = items; return order; } // 使用: // Order order = Order.newOrder() // .id(1L) // .orderNumber("ORD-001") // .items(items) // .create(); }4.3.3 @SuperBuilder 继承支持
java
import lombok.experimental.SuperBuilder; // 父类 @SuperBuilder public abstract class BaseEntity { private Long id; private LocalDateTime createTime; private LocalDateTime updateTime; } // 子类 @SuperBuilder public class User extends BaseEntity { private String username; private String email; // 使用: // User user = User.builder() // .id(1L) // .createTime(LocalDateTime.now()) // .username("test") // .email("test@example.com") // .build(); }4.4 @Value 和 @Data 区别
4.4.1 @Value - 不可变对象
java
import lombok.Value; import lombok.With; import lombok.experimental.NonFinal; @Value // 相当于 @Getter @AllArgsConstructor @ToString @EqualsAndHashCode public class ImmutableUser { Long id; String username; String email; @NonFinal @With String nickname; // 可以修改,返回新对象 // 生成: // private final Long id; // private final String username; // private final String email; // private String nickname; // // public ImmutableUser(Long id, String username, String email, String nickname) { ... } // public ImmutableUser withNickname(String nickname) { ... } // 只有 getter,没有 setter } // 使用: ImmutableUser user = new ImmutableUser(1L, "user1", "email@example.com", "nick"); ImmutableUser updated = user.withNickname("newNickname"); // 返回新对象4.4.2 @Data - 可变对象
java
import lombok.Data; @Data public class MutableUser { private Long id; private String username; private String email; // 生成: // private Long id; // private String username; // private String email; // // public MutableUser() {} // public MutableUser(Long id, String username, String email) { ... } // getter/setter/equals/hashCode/toString } // 使用: MutableUser user = new MutableUser(); user.setId(1L); user.setUsername("user1");4.5 日志注解
4.5.1 不同日志框架支持
java
// SLF4J import lombok.extern.slf4j.Slf4j; @Slf4j public class UserService { public void createUser(User user) { log.info("Creating user: {}", user.getUsername()); log.debug("User details: {}", user); // ... } } // Log4j2 import lombok.extern.log4j.Log4j2; @Log4j2 public class OrderService { public void processOrder(Order order) { log.info("Processing order: {}", order.getId()); } } // Jakarta Commons Logging import lombok.extern.java.Log; import lombok.extern.apachecommons.CommonsLog; @CommonsLog public class ProductService { public void updateProduct(Product product) { log.info("Updating product: " + product.getName()); } } // 自定义日志名称 import lombok.CustomLog; @CustomLog(topic = "audit") public class AuditService { public void auditAction(String action) { log.log(Level.INFO, "Audit action: " + action); } }4.6 实验性功能
4.6.1 @With - 不可变对象的修改
java
import lombok.With; import lombok.Value; import lombok.experimental.NonFinal; @Value public class Configuration { String host; int port; @With @NonFinal boolean sslEnabled; @With @NonFinal int timeout; } // 使用: Configuration config = new Configuration("localhost", 8080, false, 30); Configuration secureConfig = config.withSslEnabled(true); Configuration timeoutConfig = config.withTimeout(60);4.6.2 @Accessors - 链式调用
java
import lombok.Data; import lombok.experimental.Accessors; @Data @Accessors(chain = true, fluent = true) public class Product { private Long id; private String name; private Double price; // 生成链式 setter // public Product id(Long id) { this.id = id; return this; } // public Product name(String name) { this.name = name; return this; } } // 使用: Product product = new Product() .id(1L) .name("Laptop") .price(5999.99);4.7 自定义配置
4.7.1 lombok.config 配置文件
properties
# lombok.config # 全局配置 # 添加 Jackson 注解 lombok.addLombokGeneratedAnnotation = true # 生成 getter 时不使用 "get" 前缀 (boolean 类型除外) lombok.accessors.prefix += m_ # 生成链式 setter lombok.accessors.chain = true # 生成 builder 时使用 setter 方法 lombok.builder.useSetterPrefix = true # 日志配置 lombok.log.fieldName = LOGGER lombok.log.fieldIsStatic = true # 禁止特定注解的警告 lombok.extern.findbugs.addSuppressFBWarnings = true
4.7.2 目录级配置
text
project/ ├── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── example/ │ │ │ ├── web/ # web 层配置 │ │ │ │ └── lombok.config │ │ │ ├── service/ # 服务层配置 │ │ │ │ └── lombok.config │ │ │ └── entity/ # 实体层配置 │ │ │ └── lombok.config │ │ └── resources/ │ └── test/ └── lombok.config # 根配置
第五部分:Lombok 与 Spring Boot 集成最佳实践
5.1 项目结构规范
5.1.1 分层架构中的 Lombok 使用
text
src/main/java/com/example/demo/ ├── annotation/ # 自定义注解 ├── config/ # 配置类 │ ├── WebConfig.java │ ├── SecurityConfig.java │ └── SwaggerConfig.java ├── controller/ # 控制器层 │ ├── UserController.java │ ├── ProductController.java │ └── OrderController.java ├── service/ # 服务层 │ ├── UserService.java │ ├── impl/ │ │ ├── UserServiceImpl.java │ │ └── OrderServiceImpl.java ├── repository/ # 数据访问层 │ ├── UserRepository.java │ └── OrderRepository.java ├── entity/ # 实体类 │ ├── User.java │ ├── Product.java │ └── Order.java ├── dto/ # 数据传输对象 │ ├── request/ │ │ ├── CreateUserRequest.java │ │ └── UpdateUserRequest.java │ └── response/ │ ├── UserResponse.java │ └── ApiResponse.java ├── mapper/ # 对象映射 │ ├── UserMapper.java │ └── OrderMapper.java ├── exception/ # 异常类 │ ├── BusinessException.java │ └── GlobalExceptionHandler.java └── util/ # 工具类 ├── DateUtil.java └── SecurityUtil.java
5.1.2 各层 Lombok 注解规范
实体层(Entity):
java
@Entity @Table(name = "users") @Data @Builder @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode(callSuper = true) // 如果有父类 @ToString(callSuper = true) public class User extends BaseEntity { // 字段定义 }DTO 层:
java
// 请求 DTO @Data @Builder @NoArgsConstructor @AllArgsConstructor public class CreateUserRequest { @NotBlank private String username; @Email private String email; @Size(min = 6, max = 20) private String password; } // 响应 DTO @Value @Builder public class UserResponse { Long id; String username; String email; LocalDateTime createTime; }服务层:
java
@Service @Slf4j @RequiredArgsConstructor @Transactional(readOnly = true) public class UserServiceImpl implements UserService { private final UserRepository userRepository; private final UserMapper userMapper; @Override @Transactional public UserResponse createUser(CreateUserRequest request) { // 业务逻辑 } }5.2 性能优化
5.2.1 避免不必要的生成
java
// 不推荐:生成所有方法,即使不需要 @Data public class SimpleValue { private final String value; // 不需要 setter,但 @Data 还是会生成 } // 推荐:只生成需要的方法 @Getter @ToString @EqualsAndHashCode @RequiredArgsConstructor public class SimpleValue { private final String value; }5.2.2 合理使用 @EqualsAndHashCode
java
// 大型对象避免在 equals/hashCode 中包含所有字段 @Entity @Data @EqualsAndHashCode( callSuper = true, of = {"id", "code"} // 只包含唯一标识字段 ) public class Order extends BaseEntity { @Id @GeneratedValue private Long id; private String orderCode; @OneToMany(mappedBy = "order") private List<OrderItem> items; // 不包含在 equals/hashCode 中 private String customerName; private String shippingAddress; // ... 其他字段 }5.3 安全注意事项
5.3.1 敏感数据保护
java
@Data @Entity public class User { @Id @GeneratedValue private Long id; private String username; @ToString.Exclude // toString 中排除密码 @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) // 序列化时排除 private String password; private String email; @ToString.Include(name = "email") // 自定义 toString 中的显示 private String maskedEmail() { return email != null ? "***" + email.substring(email.indexOf('@')) : null; } }5.3.2 防止序列化攻击
java
@Data @Entity public class User implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue private Long id; @Setter(AccessLevel.NONE) // 禁止从外部设置 id private String secureField; // 自定义序列化 @Serial private void writeObject(ObjectOutputStream oos) throws IOException { // 自定义序列化逻辑 } @Serial private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { // 自定义反序列化逻辑 } }5.4 测试策略
5.4.1 单元测试中的 Lombok
java
// 测试实体类 @ExtendWith(MockitoExtension.class) class UserServiceTest { @Mock private UserRepository userRepository; @Mock private UserMapper userMapper; @InjectMocks private UserServiceImpl userService; @Test @DisplayName("创建用户 - 成功") void createUser_Success() { // 准备测试数据 CreateUserRequest request = CreateUserRequest.builder() .username("testuser") .email("test@example.com") .password("password123") .build(); User user = User.builder() .id(1L) .username("testuser") .email("test@example.com") .build(); UserResponse expectedResponse = UserResponse.builder() .id(1L) .username("testuser") .email("test@example.com") .build(); // Mock 行为 when(userRepository.existsByUsername(anyString())).thenReturn(false); when(userRepository.existsByEmail(anyString())).thenReturn(false); when(userMapper.toEntity(any(CreateUserRequest.class))).thenReturn(user); when(userRepository.save(any(User.class))).thenReturn(user); when(userMapper.toResponse(any(User.class))).thenReturn(expectedResponse); // 执行测试 UserResponse actualResponse = userService.createUser(request); // 验证结果 assertThat(actualResponse) .isNotNull() .extracting(UserResponse::getId, UserResponse::getUsername) .containsExactly(1L, "testuser"); verify(userRepository).save(any(User.class)); } }5.4.2 集成测试
java
@SpringBootTest @AutoConfigureMockMvc @TestInstance(TestInstance.Lifecycle.PER_CLASS) @Slf4j class UserControllerIntegrationTest { @Autowired private MockMvc mockMvc; @Autowired private ObjectMapper objectMapper; @Autowired private UserRepository userRepository; @BeforeAll void setup() { // 初始化测试数据 User user = User.builder() .username("existinguser") .email("existing@example.com") .password("password123") .build(); userRepository.save(user); } @AfterAll void cleanup() { userRepository.deleteAll(); } @Test @DisplayName("创建用户 - 集成测试") void createUser_IntegrationTest() throws Exception { // 准备请求 CreateUserRequest request = CreateUserRequest.builder() .username("newuser") .email("new@example.com") .password("newpassword123") .build(); // 执行请求 MvcResult result = mockMvc.perform(post("/api/users") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request))) .andExpect(status().isCreated()) .andExpect(jsonPath("$.success").value(true)) .andExpect(jsonPath("$.data.username").value("newuser")) .andReturn(); // 验证日志 log.info("Response: {}", result.getResponse().getContentAsString()); // 验证数据库 Optional<User> savedUser = userRepository.findByUsername("newuser"); assertThat(savedUser).isPresent(); assertThat(savedUser.get().getEmail()).isEqualTo("new@example.com"); } }第六部分:常见问题与解决方案
6.1 编译问题
6.1.1 "Cannot find symbol" 错误
问题描述:
bash
error: cannot find symbol user.setName("test"); ^ symbol: method setName(String) location: variable user of type User解决方案:
检查 IDE 插件是否安装
IntelliJ IDEA:
File → Settings → Plugins,确保 Lombok 插件已启用Eclipse: 检查
eclipse.ini中是否有-javaagent:lombok.jar
检查注解处理器配置
xml
<!-- Maven 配置 --> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <annotationProcessorPaths> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> </path> </annotationProcessorPaths> </configuration> </plugin> </plugins> </build>清理并重新编译
bash
# Maven mvn clean compile # Gradle gradle clean build
6.1.2 与 MapStruct 集成问题
问题描述:Lombok 和 MapStruct 都是注解处理器,可能发生冲突。
解决方案:
Maven 配置:
xml
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <annotationProcessorPaths> <!-- Lombok 必须在 MapStruct 之前 --> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> </path> <path> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${mapstruct.version}</version> </path> </annotationProcessorPaths> </configuration> </plugin> </plugins> </build>Gradle 配置:
gradle
dependencies { compileOnly 'org.projectlombok:lombok:1.18.30' annotationProcessor 'org.projectlombok:lombok:1.18.30' implementation 'org.mapstruct:mapstruct:1.5.5.Final' annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final' } // 确保处理顺序 tasks.withType(JavaCompile) { options.annotationProcessorPath = configurations.annotationProcessor }6.2 运行时问题
6.2.1 Jackson 序列化问题
问题描述:使用 @Builder 后,Jackson 无法反序列化。
解决方案:
java
// 添加 @Jacksonized 注解(Lombok 1.18.14+) import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; import lombok.Builder; import lombok.Value; import lombok.extern.jackson.Jacksonized; @Value @Builder @Jacksonized // 添加 Jackson 支持 public class User { Long id; String username; String email; // 或者手动配置 @JsonDeserialize(builder = User.UserBuilder.class) public User(Long id, String username, String email) { this.id = id; this.username = username; this.email = email; } @JsonPOJOBuilder(withPrefix = "") public static class UserBuilder { // Jackson 需要的构建器 } }6.2.2 JPA 延迟加载问题
问题描述:使用 @ToString 可能导致 LazyInitializationException。
解决方案:
java
@Entity @Data @ToString(exclude = {"orders", "roles"}) // 排除延迟加载的字段 public class User { @Id @GeneratedValue private Long id; private String username; @OneToMany(mappedBy = "user", fetch = FetchType.LAZY) private List<Order> orders; @ManyToMany(fetch = FetchType.LAZY) private Set<Role> roles; }6.3 IDE 问题
6.3.1 IntelliJ IDEA 代码提示问题
问题:代码提示中看不到 Lombok 生成的方法。
解决方案:
启用注解处理
File → Settings → Build, Execution, Deployment → Compiler → Annotation Processors勾选 "Enable annotation processing"
重新构建项目
Build → Rebuild Project
清理缓存
File → Invalidate Caches...选择 "Invalidate and Restart"
6.3.2 Eclipse 编译错误
问题:Eclipse 中显示编译错误,但 Maven 编译正常。
解决方案:
更新 lombok.jar
bash
java -jar lombok.jar
检查项目配置
右键项目 →
Properties → Java Compiler → Annotation Processing启用 annotation processing
更新项目
Project → Clean...
6.4 性能优化
6.4.1 减少生成的字节码大小
java
// 优化前:生成所有方法 @Data public class LargeEntity { private String field1; private String field2; // ... 30个字段 } // 优化后:只生成需要的方法 @Getter @Setter @EqualsAndHashCode(onlyExplicitlyIncluded = true) @ToString(onlyExplicitlyIncluded = true) public class LargeEntity { @EqualsAndHashCode.Include @ToString.Include private Long id; private String field1; private String field2; // ... 其他字段 }6.4.2 使用 @Builder.Default 优化
java
// 优化对象创建性能 @Builder public class Order { @Builder.Default private OrderStatus status = OrderStatus.PENDING; @Builder.Default private LocalDateTime createTime = LocalDateTime.now(); @Builder.Default private List<OrderItem> items = new ArrayList<>(); }6.5 与其他框架集成
6.5.1 与 Spring Boot Validation 集成
java
@Data @Builder @NoArgsConstructor @AllArgsConstructor public class CreateUserRequest { @NotBlank(message = "用户名不能为空") @Size(min = 3, max = 20, message = "用户名长度必须在3-20之间") private String username; @NotBlank(message = "邮箱不能为空") @Email(message = "邮箱格式不正确") private String email; @NotBlank(message = "密码不能为空") @Pattern( regexp = "^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d@$!%*#?&]{8,}$", message = "密码必须包含字母和数字,至少8位" ) private String password; @AssertTrue(message = "必须接受用户协议") private Boolean acceptTerms; }6.5.2 与 Swagger/OpenAPI 集成
java
@Data @Builder @NoArgsConstructor @AllArgsConstructor @Schema(description = "用户创建请求") public class CreateUserRequest { @Schema( description = "用户名", example = "zhangsan", requiredMode = Schema.RequiredMode.REQUIRED ) @NotBlank private String username; @Schema( description = "邮箱地址", example = "zhangsan@example.com", requiredMode = Schema.RequiredMode.REQUIRED ) @Email private String email; @Schema( description = "密码", example = "password123", requiredMode = Schema.RequiredMode.REQUIRED, minLength = 6, maxLength = 20 ) @Size(min = 6, max = 20) private String password; }第七部分:Lombok 高级技巧与实战案例
7.1 自定义 Lombok 注解
7.1.1 创建组合注解
java
// 自定义注解 - 用于 JPA 实体 @Target(ElementType.TYPE) @Retention(RetentionPolicy.SOURCE) @Data @Builder @NoArgsConstructor @AllArgsConstructor @Entity public @interface JpaEntity { String tableName() default ""; } // 使用自定义注解 @JpaEntity(tableName = "users") public class User { @Id @GeneratedValue private Long id; private String username; private String email; }7.1.2 使用 Lombok 的扩展机制
java
// 自定义 Lombok 处理器 @MetaInfServices(Processor.class) @SupportedAnnotationTypes("com.example.annotation.*") @SupportedSourceVersion(SourceVersion.RELEASE_11) public class CustomLombokProcessor extends AbstractProcessor { @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // 自定义处理逻辑 return true; } }7.2 性能敏感场景优化
7.2.1 高并发环境下的 Lombok 使用
java
// 使用 @Value 创建不可变对象,线程安全 @Value @Builder public class ImmutableConfig { String databaseUrl; String username; String password; int connectionPoolSize; // 静态工厂方法,避免反射 public static ImmutableConfig create(String url, String user, String pass, int poolSize) { return ImmutableConfig.builder() .databaseUrl(url) .username(user) .password(pass) .connectionPoolSize(poolSize) .build(); } } // 缓存配置实例,避免重复创建 public class ConfigCache { private static final Map<String, ImmutableConfig> cache = new ConcurrentHashMap<>(); public static ImmutableConfig getConfig(String key) { return cache.computeIfAbsent(key, k -> loadConfig(k)); } private static ImmutableConfig loadConfig(String key) { // 从数据库或配置中心加载 return ImmutableConfig.builder() .databaseUrl("jdbc:mysql://localhost:3306/db") .username("root") .password("password") .connectionPoolSize(10) .build(); } }7.2.2 内存优化
java
// 使用 @Data 时注意 equals/hashCode 的性能 @Entity @Data @EqualsAndHashCode( onlyExplicitlyIncluded = true, // 只包含指定的字段 callSuper = false ) public class LargeEntity { @EqualsAndHashCode.Include @Id @GeneratedValue private Long id; // 唯一标识,参与 equals/hashCode private String field1; private String field2; // ... 很多字段 @OneToMany(mappedBy = "entity") @ToString.Exclude @EqualsAndHashCode.Exclude private List<Detail> details; // 不参与 equals/hashCode // 自定义 toString,避免循环引用 @Override public String toString() { return String.format("LargeEntity{id=%d, field1='%s', field2='%s'}", id, field1, field2); } }7.3 微服务架构中的 Lombok 实践
7.3.1 共享 DTO 模块
java
// shared-dto 模块 // UserDTO.java @Data @Builder @NoArgsConstructor @AllArgsConstructor @Schema(description = "用户信息") public class UserDTO implements Serializable { private static final long serialVersionUID = 1L; @Schema(description = "用户ID") private Long id; @Schema(description = "用户名") private String username; @Schema(description = "邮箱") private String email; @Schema(description = "创建时间") private LocalDateTime createTime; } // ApiResponse.java @Data @Builder @NoArgsConstructor @AllArgsConstructor public class ApiResponse<T> implements Serializable { private static final long serialVersionUID = 1L; private boolean success; private String code; private String message; private T data; private LocalDateTime timestamp; public static <T> ApiResponse<T> success(T data) { return ApiResponse.<T>builder() .success(true) .code("SUCCESS") .message("操作成功") .data(data) .timestamp(LocalDateTime.now()) .build(); } }7.3.2 Feign Client 集成
java
// UserServiceClient.java @FeignClient(name = "user-service", path = "/api/users") public interface UserServiceClient { @PostMapping ApiResponse<UserDTO> createUser(@RequestBody CreateUserRequest request); @GetMapping("/{id}") ApiResponse<UserDTO> getUserById(@PathVariable Long id); @GetMapping ApiResponse<PageResult<UserDTO>> getUsers( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size); } // PageResult.java @Data @Builder @NoArgsConstructor @AllArgsConstructor public class PageResult<T> { private List<T> content; private int page; private int size; private long totalElements; private int totalPages; private boolean first; private boolean last; }7.4 领域驱动设计(DDD)中的 Lombok
7.4.1 值对象(Value Object)
java
// 值对象 - 不可变 @Value @Builder public class Money implements Comparable<Money> { BigDecimal amount; Currency currency; public Money add(Money other) { if (!currency.equals(other.currency)) { throw new IllegalArgumentException("货币类型不一致"); } return Money.builder() .amount(amount.add(other.amount)) .currency(currency) .build(); } public Money subtract(Money other) { if (!currency.equals(other.currency)) { throw new IllegalArgumentException("货币类型不一致"); } return Money.builder() .amount(amount.subtract(other.amount)) .currency(currency) .build(); } @Override public int compareTo(Money other) { if (!currency.equals(other.currency)) { throw new IllegalArgumentException("货币类型不一致"); } return amount.compareTo(other.amount); } }7.4.2 实体(Entity)
java
// 实体 - 有标识 @Entity @Table(name = "orders") @Data @Builder @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode(onlyExplicitlyIncluded = true) public class Order { @EqualsAndHashCode.Include @Id @GeneratedValue private Long id; @Embedded private OrderNumber orderNumber; // 值对象 @Embedded private Money totalAmount; // 值对象 @Enumerated(EnumType.STRING) private OrderStatus status; @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true) @Builder.Default private List<OrderItem> items = new ArrayList<>(); // 领域方法 public void addItem(Product product, int quantity) { OrderItem item = OrderItem.builder() .order(this) .product(product) .quantity(quantity) .unitPrice(product.getPrice()) .build(); items.add(item); calculateTotal(); } private void calculateTotal() { BigDecimal total = items.stream() .map(OrderItem::getSubtotal) .reduce(BigDecimal.ZERO, BigDecimal::add); this.totalAmount = Money.builder() .amount(total) .currency(Currency.getInstance("CNY")) .build(); } // 值对象 @Embeddable @Value @Builder public static class OrderNumber { String value; public OrderNumber(String value) { // 验证逻辑 if (value == null || !value.matches("ORD-\\d{8}-\\d{6}")) { throw new IllegalArgumentException("订单号格式不正确"); } this.value = value; } } }7.5 响应式编程中的 Lombok
7.5.1 WebFlux + Lombok
java
// 响应式实体 @Data @Builder @NoArgsConstructor @AllArgsConstructor public class ReactiveUser { private Long id; private String username; private String email; private LocalDateTime createTime; } // 响应式控制器 @RestController @RequestMapping("/api/reactive/users") @RequiredArgsConstructor @Slf4j public class ReactiveUserController { private final ReactiveUserService userService; @PostMapping public Mono<ApiResponse<ReactiveUser>> createUser( @Valid @RequestBody CreateUserRequest request) { return userService.createUser(request) .map(user -> ApiResponse.success(user)) .doOnSubscribe(s -> log.info("Creating user: {}", request.getUsername())) .doOnSuccess(r -> log.info("User created successfully")) .doOnError(e -> log.error("Failed to create user", e)); } @GetMapping("/{id}") public Mono<ApiResponse<ReactiveUser>> getUserById(@PathVariable Long id) { return userService.getUserById(id) .map(ApiResponse::success) .switchIfEmpty(Mono.just(ApiResponse.error("用户不存在"))); } @GetMapping public Flux<ReactiveUser> getUsers( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size) { return userService.getUsers(page, size) .doOnNext(user -> log.debug("Emitting user: {}", user.getUsername())); } }7.5.2 响应式服务
java
@Service @RequiredArgsConstructor @Slf4j public class ReactiveUserServiceImpl implements ReactiveUserService { private final ReactiveUserRepository userRepository; private final ReactivePasswordEncoder passwordEncoder; @Override public Mono<ReactiveUser> createUser(CreateUserRequest request) { return userRepository.existsByUsername(request.getUsername()) .flatMap(exists -> { if (exists) { return Mono.error(new BusinessException("用户名已存在")); } return userRepository.existsByEmail(request.getEmail()); }) .flatMap(exists -> { if (exists) { return Mono.error(new BusinessException("邮箱已存在")); } return passwordEncoder.encode(request.getPassword()) .flatMap(encodedPassword -> { ReactiveUser user = ReactiveUser.builder() .username(request.getUsername()) .email(request.getEmail()) .password(encodedPassword) .createTime(LocalDateTime.now()) .build(); return userRepository.save(user); }); }) .doOnSuccess(user -> log.info("User created: {}", user.getUsername())) .doOnError(error -> log.error("Failed to create user", error)); } }7.6 监控与日志
7.6.1 结构化日志
java
@Service @Slf4j @RequiredArgsConstructor public class AuditService { @Value("${spring.application.name}") private String applicationName; public void auditAction(String action, String userId, Object data) { AuditLog auditLog = AuditLog.builder() .application(applicationName) .action(action) .userId(userId) .timestamp(LocalDateTime.now()) .data(data) .build(); // 结构化日志 log.info("Audit log: {}", auditLog); // 或者使用 MDC MDC.put("userId", userId); MDC.put("action", action); log.info("User action performed"); MDC.clear(); } @Data @Builder @NoArgsConstructor @AllArgsConstructor public static class AuditLog { private String application; private String action; private String userId; private LocalDateTime timestamp; private Object data; @Override public String toString() { return String.format( "AuditLog{application='%s', action='%s', userId='%s', timestamp=%s}", application, action, userId, timestamp ); } } }7.6.2 性能监控
java
@Component @Slf4j @RequiredArgsConstructor public class PerformanceMonitor { private final MeterRegistry meterRegistry; @Data @Builder public static class PerformanceMetrics { private String methodName; private long executionTime; private boolean success; private String errorMessage; private LocalDateTime timestamp; } public <T> T monitor(String methodName, Supplier<T> operation) { long startTime = System.currentTimeMillis(); boolean success = true; String errorMessage = null; try { return operation.get(); } catch (Exception e) { success = false; errorMessage = e.getMessage(); throw e; } finally { long endTime = System.currentTimeMillis(); long executionTime = endTime - startTime; // 记录指标 meterRegistry.timer("method.execution.time", "method", methodName) .record(executionTime, TimeUnit.MILLISECONDS); // 记录日志 PerformanceMetrics metrics = PerformanceMetrics.builder() .methodName(methodName) .executionTime(executionTime) .success(success) .errorMessage(errorMessage) .timestamp(LocalDateTime.now()) .build(); if (executionTime > 1000) { log.warn("Method {} took {} ms", methodName, executionTime); } else { log.debug("Method performance: {}", metrics); } } } }第八部分:总结
8.1 Lombok 在 Spring Boot 项目中的价值总结
8.1.1 主要优势
开发效率提升:减少 60-80% 的样板代码
代码可读性:类定义更加简洁,聚焦业务逻辑
维护成本降低:自动生成的方法始终保持一致性
错误减少:避免手写代码导致的错误
团队协作:统一代码风格,减少争议
8.1.2 适用场景
实体类:JPA 实体、MongoDB 文档等
DTO/VO:数据传输对象、视图对象
配置类:Spring Boot 配置属性类
服务类:业务逻辑服务层
工具类:各种辅助工具类
8.2 最佳实践总结
8.2.1 注解选择指南
| 场景 | 推荐注解 | 说明 |
|---|---|---|
| 可变实体类 | @Data @Builder | 适合大多数实体类 |
| 不可变对象 | @Value @With | 适合配置类、值对象 |
| 服务类 | @Slf4j @RequiredArgsConstructor | 依赖注入和日志 |
| 工具类 | @UtilityClass | 静态工具类 |
| 枚举类 | @AllArgsConstructor @Getter | 枚举定义 |
8.2.2 配置建议
IDE 配置:必须安装插件,启用注解处理
构建工具:正确配置注解处理器路径
版本管理:使用稳定版本,及时更新
团队规范:制定统一的 Lombok 使用规范
8.3 注意事项
不要过度使用:只在需要的地方使用 Lombok
注意继承关系:合理配置 callSuper 参数
考虑序列化:注意 Jackson 等框架的兼容性
性能考量:大型对象注意 equals/hashCode 的性能
团队共识:确保团队成员都熟悉 Lombok
8.4 未来展望
Lombok 持续发展,新的功能不断加入:
Record 支持:Java 14+ 的 Record 类型
更多实验性功能:@StandardException 等
更好的 IDE 集成:更智能的代码提示
性能优化:生成更高效的字节码
8.5 学习资源
官方文档:https://projectlombok.org/
GitHub 仓库:https://github.com/projectlombok/lombok
Stack Overflow:大量实际问题的解决方案
Spring 官方文档:Spring Boot 最佳实践