Spring Boot 4.0 整合 MyBatis-Plus 完整教程

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 关键特性使用

  1. 虚拟线程 :通过 Thread.ofVirtual()和虚拟线程池优化并发性能

  2. 响应式编程:结合 MyBatis-Plus 和 R2DBC 实现非阻塞数据访问

  3. 结构化并发:使用 Java 21 的结构化并发 API

  4. AOT 编译:支持 GraalVM 原生镜像,提升启动速度

  5. 模块化:更好的 Java Module 支持

15.2 性能优化建议

  1. 对于 I/O 密集型操作,使用虚拟线程

  2. 对于高并发场景,使用响应式编程

  3. 使用 GraalVM 原生镜像减少内存占用和启动时间

  4. 合理配置连接池和线程池参数

15.3 注意事项

  1. Spring Boot 4.0 目前是预览版,API 可能会有变化

  2. 虚拟线程对阻塞操作敏感,避免在虚拟线程中执行长时间 CPU 操作

  3. 原生镜像编译需要处理反射和动态代理的注册

  4. 生产环境建议充分测试响应式和虚拟线程的稳定性

这个教程展示了如何利用 Spring Boot 4.0 的新特性(虚拟线程、响应式编程、AOT 编译等)来整合 MyBatis-Plus,构建高性能的现代 Java 应用程序。

相关推荐
梦未5 小时前
Spring控制反转与依赖注入
java·后端·spring
喜欢流萤吖~5 小时前
Lambda 表达式
java
无限大65 小时前
验证码对抗史
后端
ZouZou老师5 小时前
C++设计模式之适配器模式:以家具生产为例
java·设计模式·适配器模式
用户2190326527355 小时前
Java后端必须的Docker 部署 Redis 集群完整指南
linux·后端
曼巴UE56 小时前
UE5 C++ 动态多播
java·开发语言
VX:Fegn08956 小时前
计算机毕业设计|基于springboot + vue音乐管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·课程设计
程序员鱼皮6 小时前
刚刚,IDEA 免费版发布!终于不用破解了
java·程序员·jetbrains