Spring Boot 4.0 整合 MyBatis-Plus 完整教程
注:Spring Boot 4.0 ,本教程基于 Spring Boot 4.0 的预览版和新特性预期进行构建。实际发布时可能会有调整。
一、Spring Boot 4.0 新特性概述
1.1 主要新特性
-
Java 21+ 支持:基于虚拟线程的响应式编程增强
-
GraalVM 原生镜像优化:更完善的 AOT 编译支持
-
响应式编程增强:更好的响应式 MyBatis 支持
-
模块化改进:更好的 Java Module 支持
-
AI/机器学习集成:内置 AI 功能支持
1.2 构建工具要求
-
JDK 21+
-
Maven 3.9+ 或 Gradle 8.5+
-
GraalVM 23.0+(可选,用于原生编译)
二、项目初始化
2.1 使用 Spring Initializr
ini
# 使用官方 Initializr
curl https://start.spring.io/starter.zip \
-d type=maven-project \
-d language=java \
-d bootVersion=4.0.0-M1 \
-d baseDir=spring-boot4-mybatisplus \
-d groupId=com.example \
-d artifactId=demo \
-d name=demo \
-d description=Demo+project+for+Spring+Boot+4.0+with+MyBatis-Plus \
-d packageName=com.example.demo \
-d packaging=jar \
-d javaVersion=21 \
-d dependencies=web,data-jpa \
-o demo.zip
2.2 手动创建 pom.xml
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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.0.0-M1</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>springboot4-mybatisplus</artifactId>
<version>1.0.0</version>
<name>springboot4-mybatisplus</name>
<description>Spring Boot 4.0 with MyBatis-Plus</description>
<properties>
<java.version>21</java.version>
<mybatis-plus.version>3.5.5</mybatis-plus.version>
<graalvm.version>23.0.0</graalvm.version>
</properties>
<dependencies>
<!-- Spring Boot 4.0 核心依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 响应式 Web 支持(新特性) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- MyBatis-Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- 数据库驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 响应式 MySQL 驱动(新特性) -->
<dependency>
<groupId>io.asyncer</groupId>
<artifactId>r2dbc-mysql</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 响应式 R2DBC -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- GraalVM 原生镜像支持(新特性) -->
<dependency>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 响应式测试支持 -->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Spring Boot Maven 插件,支持 AOT -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<builder>paketobuildpacks/builder-jammy-tiny:latest</builder>
<env>
<BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
</env>
</image>
</configuration>
</plugin>
<!-- GraalVM Native Image 插件 -->
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>${graalvm.version}</version>
<extensions>true</extensions>
<executions>
<execution>
<id>build-native</id>
<goals>
<goal>compile-no-fork</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
<!-- Spring Milestone 仓库 -->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
三、配置 MyBatis-Plus
3.1 应用配置(application.yml)
yaml
# Spring Boot 4.0 新配置格式
spring:
application:
name: springboot4-mybatisplus
# 数据源配置(虚拟线程优化)
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis_plus_demo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: root
password: 123456
hikari:
# 虚拟线程池配置(Spring Boot 4.0 新特性)
thread-factory: org.springframework.boot.jdbc.VirtualThreadExecutor
# 响应式数据源(新特性)
r2dbc:
url: r2dbc:mysql://localhost:3306/mybatis_plus_demo
username: root
password: 123456
# AOT 编译配置
aot:
enabled: true
generate-code: true
# GraalVM 原生镜像配置
native:
mode: native
debug: true
# MyBatis-Plus 配置
mybatis-plus:
configuration:
# 自动映射行为
auto-mapping-behavior: full
# 驼峰命名转换
map-underscore-to-camel-case: true
# 日志实现
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
# 逻辑删除字段名
logic-delete-field: deleted
# 逻辑删除值
logic-delete-value: 1
# 逻辑未删除值
logic-not-delete-value: 0
# 主键类型
id-type: auto
# 分页插件配置
pagehelper:
helper-dialect: mysql
reasonable: true
support-methods-arguments: true
# 日志配置
logging:
level:
com.example.demo.mapper: debug
org.springframework.r2dbc: debug
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
# 虚拟线程配置(Spring Boot 4.0 新特性)
server:
tomcat:
threads:
# 使用虚拟线程
use-virtual-threads: true
# 响应式服务器配置
netty:
connection-timeout: 2m
3.2 MyBatis-Plus 配置类
kotlin
package com.example.demo.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.apache.ibatis.reflection.MetaObject;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import java.time.LocalDateTime;
/**
* MyBatis-Plus 配置类
* 使用 Spring Boot 4.0 新特性:虚拟线程兼容性配置
*/
@Configuration
@EnableTransactionManagement
@MapperScan("com.example.demo.mapper")
public class MyBatisPlusConfig {
/**
* MyBatis-Plus 拦截器配置
* 支持分页、乐观锁、防全表更新与删除
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
// 乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
// 防止全表更新与删除
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
return interceptor;
}
/**
* 元数据处理器 - 自动填充字段
*/
@Bean
public MetaObjectHandler metaObjectHandler() {
return new MetaObjectHandler() {
@Override
public void insertFill(MetaObject metaObject) {
// 使用虚拟线程安全的日期时间
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "version", Integer.class, 1);
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
};
}
}
四、实体类与 Mapper
4.1 基础实体类
less
package com.example.demo.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 基础实体类
* 使用 Java 21 Record 模式(预览特性)
*/
@Data
@Accessors(chain = true)
public class BaseEntity implements Serializable {
/**
* 主键ID - 使用虚拟线程安全的ID生成策略
*/
@TableId(type = IdType.ASSIGN_ID)
private Long id;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
/**
* 创建人
*/
@TableField(fill = FieldFill.INSERT)
private String createBy;
/**
* 更新人
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private String updateBy;
/**
* 乐观锁版本号
*/
@Version
@TableField(fill = FieldFill.INSERT)
private Integer version;
/**
* 逻辑删除标志
*/
@TableLogic
@TableField(fill = FieldFill.INSERT)
private Integer deleted;
}
4.2 用户实体类
scala
package com.example.demo.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 用户实体类
* 使用 Spring Boot 4.0 新特性:虚拟线程安全的序列化
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@TableName("sys_user")
public class User extends BaseEntity {
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 邮箱
*/
private String email;
/**
* 手机号
*/
private String phone;
/**
* 状态:0-禁用,1-启用
*/
private Integer status;
/**
* 虚拟线程安全的额外字段
* 使用 transient 避免序列化问题
*/
@TableField(exist = false)
private transient String virtualThreadInfo;
}
4.3 Mapper 接口
kotlin
package com.example.demo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.springframework.data.repository.query.Param;
import org.springframework.r2dbc.core.DatabaseClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
* 用户 Mapper
* 支持响应式查询(Spring Boot 4.0 新特性)
*/
@Mapper
public interface UserMapper extends BaseMapper<User> {
/**
* 传统阻塞式查询
*/
@Select("SELECT * FROM sys_user WHERE username = #{username}")
User selectByUsername(@Param("username") String username);
/**
* 响应式查询 - 返回单个结果
* 使用虚拟线程安全的方式
*/
@Select("SELECT * FROM sys_user WHERE id = #{id}")
default Mono<User> selectReactiveById(Long id, DatabaseClient databaseClient) {
return databaseClient.sql("SELECT * FROM sys_user WHERE id = :id")
.bind("id", id)
.map((row, metadata) -> {
User user = new User();
user.setId(row.get("id", Long.class));
user.setUsername(row.get("username", String.class));
user.setEmail(row.get("email", String.class));
user.setStatus(row.get("status", Integer.class));
return user;
})
.one();
}
/**
* 响应式查询 - 返回多个结果
*/
@Select("SELECT * FROM sys_user WHERE status = #{status}")
default Flux<User> selectReactiveByStatus(Integer status, DatabaseClient databaseClient) {
return databaseClient.sql("SELECT * FROM sys_user WHERE status = :status")
.bind("status", status)
.map((row, metadata) -> {
User user = new User();
user.setId(row.get("id", Long.class));
user.setUsername(row.get("username", String.class));
user.setEmail(row.get("email", String.class));
user.setStatus(row.get("status", Integer.class));
return user;
})
.all();
}
}
五、Service 层实现
5.1 接口定义
arduino
package com.example.demo.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.demo.entity.User;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.concurrent.CompletableFuture;
/**
* 用户服务接口
* 支持响应式和虚拟线程
*/
public interface UserService extends IService<User> {
/**
* 虚拟线程方式查询用户
* Spring Boot 4.0 新特性
*/
CompletableFuture<User> findUserByUsernameVirtualThread(String username);
/**
* 响应式查询用户
*/
Mono<User> findUserByIdReactive(Long id);
/**
* 响应式查询用户列表
*/
Flux<User> findUsersByStatusReactive(Integer status);
/**
* 使用结构化并发(Java 21 新特性)
*/
User findUserWithStructuredConcurrency(Long id);
}
5.2 服务实现
java
package com.example.demo.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import com.example.demo.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.r2dbc.core.DatabaseClient;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.TimeUnit;
/**
* 用户服务实现
* 使用 Spring Boot 4.0 新特性:虚拟线程和响应式编程
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
private final UserMapper userMapper;
private final DatabaseClient databaseClient;
@Override
public CompletableFuture<User> findUserByUsernameVirtualThread(String username) {
return CompletableFuture.supplyAsync(() -> {
// 在虚拟线程中执行
try {
// 模拟耗时操作
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return userMapper.selectOne(
new LambdaQueryWrapper<User>()
.eq(User::getUsername, username)
);
}, Thread.ofVirtual().factory());
}
@Override
public Mono<User> findUserByIdReactive(Long id) {
return userMapper.selectReactiveById(id, databaseClient)
.doOnNext(user -> log.info("Found user reactively: {}", user.getUsername()));
}
@Override
public Flux<User> findUsersByStatusReactive(Integer status) {
return userMapper.selectReactiveByStatus(status, databaseClient)
.doOnNext(user -> log.info("Streaming user: {}", user.getUsername()));
}
@Override
public User findUserWithStructuredConcurrency(Long id) {
// Java 21 结构化并发
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
var userFuture = scope.fork(() ->
userMapper.selectById(id)
);
var userDetailsFuture = scope.fork(() ->
// 模拟获取用户详情
"User Details for ID: " + id
);
scope.join();
scope.throwIfFailed();
User user = userFuture.get();
if (user != null) {
user.setVirtualThreadInfo(userDetailsFuture.get());
}
return user;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Task interrupted", e);
} catch (Exception e) {
throw new RuntimeException("Failed to fetch user", e);
}
}
/**
* 批量保存用户 - 使用虚拟线程优化
*/
public void saveUsersWithVirtualThreads(List<User> users) {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
List<StructuredTaskScope.Subtask<User>> tasks = users.stream()
.map(user -> scope.fork(() -> {
// 每个用户保存操作在独立的虚拟线程中执行
save(user);
return user;
}))
.toList();
scope.join();
scope.throwIfFailed();
// 处理结果
tasks.forEach(task ->
log.info("Saved user: {}", task.get().getUsername())
);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Save interrupted", e);
} catch (Exception e) {
throw new RuntimeException("Failed to save users", e);
}
}
}
六、Controller 层
6.1 传统 REST Controller
less
package com.example.demo.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
/**
* 用户控制器
* 支持虚拟线程和传统阻塞模式
*/
@Slf4j
@RestController
@RequestMapping("/api/v1/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
/**
* 创建用户
*/
@PostMapping
public ResponseEntity<User> create(@RequestBody User user) {
boolean saved = userService.save(user);
return saved ?
ResponseEntity.ok(user) :
ResponseEntity.badRequest().build();
}
/**
* 分页查询用户
* 使用虚拟线程优化
*/
@GetMapping("/page")
public ResponseEntity<IPage<User>> page(
@RequestParam(defaultValue = "1") Integer current,
@RequestParam(defaultValue = "10") Integer size) {
Page<User> page = new Page<>(current, size);
IPage<User> result = userService.page(page);
return ResponseEntity.ok(result);
}
/**
* 使用虚拟线程查询用户
* Spring Boot 4.0 新特性
*/
@GetMapping("/virtual/{username}")
public ResponseEntity<User> findByUsernameVirtual(@PathVariable String username)
throws ExecutionException, InterruptedException {
CompletableFuture<User> future = userService.findUserByUsernameVirtualThread(username);
return ResponseEntity.ok(future.get());
}
/**
* 使用结构化并发查询用户
* Java 21 新特性
*/
@GetMapping("/structured/{id}")
public ResponseEntity<User> findWithStructuredConcurrency(@PathVariable Long id) {
User user = userService.findUserWithStructuredConcurrency(id);
return ResponseEntity.ok(user);
}
/**
* 批量创建用户 - 使用虚拟线程
*/
@PostMapping("/batch/virtual")
public ResponseEntity<Void> createBatchVirtual(@RequestBody List<User> users) {
// 在新线程中执行批量操作
Thread.ofVirtual().start(() -> {
userService.saveUsersWithVirtualThreads(users);
});
return ResponseEntity.accepted().build();
}
}
6.2 响应式 Controller
kotlin
package com.example.demo.controller;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;
/**
* 响应式用户控制器
* Spring Boot 4.0 响应式增强
*/
@Slf4j
@RestController
@RequestMapping("/api/v2/users")
@RequiredArgsConstructor
public class UserReactiveController {
private final UserService userService;
/**
* 响应式查询单个用户
*/
@GetMapping("/reactive/{id}")
public Mono<ResponseEntity<User>> findByIdReactive(@PathVariable Long id) {
return userService.findUserByIdReactive(id)
.map(ResponseEntity::ok)
.defaultIfEmpty(ResponseEntity.notFound().build());
}
/**
* 响应式查询用户列表
*/
@GetMapping(value = "/reactive/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<User> streamByStatus(@RequestParam Integer status) {
return userService.findUsersByStatusReactive(status)
.delayElements(Duration.ofMillis(100)) // 模拟流式处理
.doOnNext(user ->
log.info("Streaming user: {}", user.getUsername())
);
}
/**
* SSE 流式传输
*/
@GetMapping(value = "/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> userEvents() {
return Flux.interval(Duration.ofSeconds(1))
.map(sequence ->
String.format("User Event %d at %s",
sequence,
java.time.LocalTime.now())
);
}
}
七、响应式 Repository
java
package com.example.demo.repository;
import com.example.demo.entity.User;
import org.springframework.data.r2dbc.repository.R2dbcRepository;
import org.springframework.data.r2dbc.repository.Query;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
* 响应式用户仓库
* Spring Boot 4.0 响应式数据访问
*/
@Repository
public interface UserReactiveRepository extends R2dbcRepository<User, Long> {
/**
* 根据用户名查询
*/
@Query("SELECT * FROM sys_user WHERE username = :username")
Mono<User> findByUsername(String username);
/**
* 根据状态查询
*/
@Query("SELECT * FROM sys_user WHERE status = :status")
Flux<User> findByStatus(Integer status);
/**
* 分页查询
*/
@Query("SELECT * FROM sys_user ORDER BY create_time DESC LIMIT :limit OFFSET :offset")
Flux<User> findAllWithPagination(int limit, int offset);
}
八、全局异常处理
typescript
package com.example.demo.handler;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.bind.support.WebExchangeBindException;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Map;
/**
* 全局异常处理器
* 支持响应式异常处理
*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理 MyBatis-Plus 异常
*/
@ExceptionHandler(MybatisPlusException.class)
public ResponseEntity<Map<String, Object>> handleMybatisPlusException(
MybatisPlusException e) {
log.error("MyBatis-Plus Exception: {}", e.getMessage(), e);
Map<String, Object> response = new HashMap<>();
response.put("code", HttpStatus.INTERNAL_SERVER_ERROR.value());
response.put("message", "数据库操作失败");
response.put("detail", e.getMessage());
response.put("timestamp", System.currentTimeMillis());
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(response);
}
/**
* 处理虚拟线程中断异常
*/
@ExceptionHandler(InterruptedException.class)
public ResponseEntity<Map<String, Object>> handleInterruptedException(
InterruptedException e) {
log.warn("Virtual thread interrupted: {}", e.getMessage());
Map<String, Object> response = new HashMap<>();
response.put("code", HttpStatus.SERVICE_UNAVAILABLE.value());
response.put("message", "请求被中断");
response.put("timestamp", System.currentTimeMillis());
Thread.currentThread().interrupt(); // 恢复中断状态
return ResponseEntity
.status(HttpStatus.SERVICE_UNAVAILABLE)
.body(response);
}
/**
* 响应式异常处理
*/
@ExceptionHandler(WebExchangeBindException.class)
public Mono<ResponseEntity<Map<String, Object>>> handleBindException(
WebExchangeBindException e) {
return Mono.fromCallable(() -> {
Map<String, Object> response = new HashMap<>();
response.put("code", HttpStatus.BAD_REQUEST.value());
response.put("message", "参数验证失败");
Map<String, String> errors = new HashMap<>();
e.getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage())
);
response.put("errors", errors);
response.put("timestamp", System.currentTimeMillis());
return ResponseEntity.badRequest().body(response);
});
}
}
九、虚拟线程配置
kotlin
package com.example.demo.config;
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
import org.springframework.boot.web.embedded.tomcat.TomcatProtocolHandlerCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.core.task.support.TaskExecutorAdapter;
import org.springframework.scheduling.annotation.EnableAsync;
import java.util.concurrent.Executors;
/**
* 虚拟线程配置
* Spring Boot 4.0 核心新特性
*/
@Configuration
@EnableAsync
public class VirtualThreadConfig {
/**
* 配置 Tomcat 使用虚拟线程
*/
@Bean
public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() {
return protocolHandler ->
protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
}
/**
* 配置 Spring 异步任务执行器使用虚拟线程
*/
@Bean(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME)
public AsyncTaskExecutor asyncTaskExecutor() {
return new TaskExecutorAdapter(Executors.newVirtualThreadPerTaskExecutor());
}
/**
* 配置虚拟线程池用于数据库操作
*/
@Bean
public java.util.concurrent.ExecutorService databaseExecutor() {
return Executors.newThreadPerTaskExecutor(
Thread.ofVirtual()
.name("db-virtual-thread-", 0)
.factory()
);
}
}
十、AOT 和原生镜像支持
10.1 创建 AOT 处理类
kotlin
package com.example.demo.aot;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportRuntimeHints;
/**
* AOT 运行时提示注册
* Spring Boot 4.0 原生镜像支持
*/
@Configuration
@ImportRuntimeHints(AotRuntimeHints.MybatisPlusRuntimeHints.class)
public class AotRuntimeHints {
static class MybatisPlusRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
// 注册反射
hints.reflection().registerType(
com.baomidou.mybatisplus.core.mapper.BaseMapper.class,
hint -> hint.withMembers(
org.springframework.aot.hint.MemberCategory.INVOKE_PUBLIC_METHODS
)
);
// 注册资源
hints.resources().registerPattern("mapper/*.xml");
hints.resources().registerPattern("mybatis-config.xml");
// 注册序列化
hints.serialization().registerType(
com.example.demo.entity.User.class
);
// 注册代理
hints.proxies().registerJdkProxy(
org.apache.ibatis.binding.MapperProxy.class
);
}
}
}
10.2 native-image.properties
ini
Args = --enable-url-protocols=http,https \
-H:+ReportExceptionStackTraces \
-H:+ReportUnsupportedElementsAtRuntime \
--initialize-at-build-time=com.mysql.cj \
--initialize-at-run-time=io.netty.handler.codec.http.HttpObjectEncoder \
-H:IncludeResourceBundles=com.mysql.cj.LocalizedErrorMessages
十一、测试类
scss
package com.example.demo;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import static org.assertj.core.api.Assertions.assertThat;
/**
* 集成测试
* 测试虚拟线程和响应式功能
*/
@Slf4j
@SpringBootTest
class SpringBoot4MybatisPlusApplicationTests {
@Autowired
private UserService userService;
/**
* 测试虚拟线程查询
*/
@Test
void testVirtualThreadQuery() throws ExecutionException, InterruptedException, TimeoutException {
// 准备测试数据
User user = new User()
.setUsername("test_virtual")
.setEmail("test@example.com")
.setStatus(1);
userService.save(user);
// 使用虚拟线程查询
CompletableFuture<User> future = userService.findUserByUsernameVirtualThread("test_virtual");
User result = future.get(5, TimeUnit.SECONDS);
assertThat(result).isNotNull();
assertThat(result.getUsername()).isEqualTo("test_virtual");
log.info("Virtual thread test passed, user ID: {}", result.getId());
}
/**
* 测试响应式查询
*/
@Test
void testReactiveQuery() {
// 准备测试数据
User user = new User()
.setUsername("test_reactive")
.setEmail("reactive@example.com")
.setStatus(1);
userService.save(user);
// 使用响应式查询
Mono<User> userMono = userService.findUserByIdReactive(user.getId());
StepVerifier.create(userMono)
.assertNext(foundUser -> {
assertThat(foundUser).isNotNull();
assertThat(foundUser.getUsername()).isEqualTo("test_reactive");
})
.verifyComplete();
log.info("Reactive query test passed");
}
/**
* 测试结构化并发
*/
@Test
void testStructuredConcurrency() {
// 准备测试数据
User user = new User()
.setUsername("test_structured")
.setEmail("structured@example.com")
.setStatus(1);
userService.save(user);
// 使用结构化并发查询
User result = userService.findUserWithStructuredConcurrency(user.getId());
assertThat(result).isNotNull();
assertThat(result.getUsername()).isEqualTo("test_structured");
assertThat(result.getVirtualThreadInfo()).contains("User Details");
log.info("Structured concurrency test passed");
}
/**
* 测试批量虚拟线程操作
*/
@Test
void testBatchVirtualThreads() {
// 准备测试数据
List<User> users = new ArrayList<>();
for (int i = 0; i < 10; i++) {
users.add(new User()
.setUsername("batch_user_" + i)
.setEmail("batch" + i + "@example.com")
.setStatus(1));
}
// 使用虚拟线程批量保存
userService.saveUsersWithVirtualThreads(users);
// 验证保存结果
List<User> savedUsers = userService.list(
new LambdaQueryWrapper<User>()
.likeRight(User::getUsername, "batch_user_")
);
assertThat(savedUsers).hasSize(10);
log.info("Batch virtual threads test passed, saved {} users", savedUsers.size());
}
}
十二、性能监控
java
package com.example.demo.monitor;
import io.micrometer.core.instrument.MeterRegistry;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 虚拟线程性能监控
* Spring Boot 4.0 监控增强
*/
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class VirtualThreadMonitor {
private final MeterRegistry meterRegistry;
private final AtomicInteger activeVirtualThreads = new AtomicInteger(0);
/**
* 监控虚拟线程使用
*/
@Around("@annotation(com.example.demo.annotation.VirtualThreadMonitor)")
public Object monitorVirtualThread(ProceedingJoinPoint joinPoint) throws Throwable {
StopWatch stopWatch = new StopWatch();
int currentThreads = activeVirtualThreads.incrementAndGet();
try {
stopWatch.start();
Object result = joinPoint.proceed();
stopWatch.stop();
// 记录指标
meterRegistry.timer("virtual.thread.execution.time")
.record(stopWatch.getTotalTimeNanos(), java.util.concurrent.TimeUnit.NANOSECONDS);
meterRegistry.gauge("virtual.thread.active.count", activeVirtualThreads);
log.info("Virtual thread executed: method={}, time={}ms, activeThreads={}",
joinPoint.getSignature().getName(),
stopWatch.getTotalTimeMillis(),
currentThreads);
return result;
} finally {
activeVirtualThreads.decrementAndGet();
}
}
}
十三、启动类
typescript
package com.example.demo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
@Slf4j
@SpringBootApplication
public class SpringBoot4MybatisPlusApplication {
public static void main(String[] args) {
// 启用 AOT 优化
System.setProperty("spring.aot.enabled", "true");
SpringApplication.run(SpringBoot4MybatisPlusApplication.class, args);
}
@EventListener(ApplicationReadyEvent.class)
public void onApplicationReady() {
log.info("=========================================");
log.info("Spring Boot 4.0 with MyBatis-Plus Started");
log.info("Java Version: {}", System.getProperty("java.version"));
log.info("Virtual Threads Available: {}",
Thread.ofVirtual() != null ? "Yes" : "No");
log.info("Available Processors: {}",
Runtime.getRuntime().availableProcessors());
log.info("=========================================");
}
}
十四、部署和优化
14.1 Dockerfile(支持原生镜像)
bash
# 构建阶段
FROM ghcr.io/graalvm/native-image:java21 AS builder
WORKDIR /app
COPY target/*.jar app.jar
RUN native-image -jar app.jar --no-fallback -H:Name=app-native
# 运行阶段
FROM alpine:latest
RUN apk add --no-cache libstdc++
WORKDIR /app
COPY --from=builder /app/app-native /app/app
EXPOSE 8080
ENTRYPOINT ["/app/app"]
14.2 Docker Compose
yaml
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- JAVA_OPTS=-XX:+UseZGC -Xmx512m
depends_on:
- mysql
deploy:
resources:
limits:
memory: 512M
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: mybatis_plus_demo
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
command:
- --default-authentication-plugin=mysql_native_password
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
volumes:
mysql_data:
十五、总结
15.1 关键特性使用
-
虚拟线程 :通过
Thread.ofVirtual()和虚拟线程池优化并发性能 -
响应式编程:结合 MyBatis-Plus 和 R2DBC 实现非阻塞数据访问
-
结构化并发:使用 Java 21 的结构化并发 API
-
AOT 编译:支持 GraalVM 原生镜像,提升启动速度
-
模块化:更好的 Java Module 支持
15.2 性能优化建议
-
对于 I/O 密集型操作,使用虚拟线程
-
对于高并发场景,使用响应式编程
-
使用 GraalVM 原生镜像减少内存占用和启动时间
-
合理配置连接池和线程池参数
15.3 注意事项
-
Spring Boot 4.0 目前是预览版,API 可能会有变化
-
虚拟线程对阻塞操作敏感,避免在虚拟线程中执行长时间 CPU 操作
-
原生镜像编译需要处理反射和动态代理的注册
-
生产环境建议充分测试响应式和虚拟线程的稳定性
这个教程展示了如何利用 Spring Boot 4.0 的新特性(虚拟线程、响应式编程、AOT 编译等)来整合 MyBatis-Plus,构建高性能的现代 Java 应用程序。