跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava

Spring Boot 4.0 新特性整合 MyBatis-Plus 完整教程

如何在 Spring Boot 4.0 预览版中整合 MyBatis-Plus。内容涵盖项目初始化、配置虚拟线程与响应式编程、实体类与 Mapper 编写、Service 层实现(含结构化并发)、Controller 层 REST API 设计、全局异常处理、AOT 原生镜像支持及 Docker 部署方案。通过示例代码展示了如何利用 Java 21 虚拟线程优化并发性能,结合 R2DBC 实现非阻塞数据访问,并提供了完整的构建工具配置与测试方法,旨在帮助开发者构建高性能的现代 Java 应用程序。

赛博朋克发布于 2026/3/22更新于 2026/8/221.4K 浏览
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
# 使用官方 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 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)
# 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 配置类
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 基础实体类
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 用户实体类
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 接口
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 接口定义
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 服务实现
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.List;
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
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
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.http.ResponseEntity;
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

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);
}

八、全局异常处理

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);
        });
    }
}

九、虚拟线程配置

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 处理类
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
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

十一、测试类

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.ArrayList;
import java.util.List;
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("[email protected]").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("[email protected]").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("[email protected]").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());
    }
}

十二、性能监控

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();
        }
    }
}

十三、启动类

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;

/**
 * Spring Boot 4.0 Application
 */
@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(支持原生镜像)
# 构建阶段
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
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 应用程序。

目录

  1. Spring Boot 4.0 整合 MyBatis-Plus 完整教程
  2. 一、Spring Boot 4.0 新特性概述
  3. 1.1 主要新特性
  4. 1.2 构建工具要求
  5. 二、项目初始化
  6. 2.1 使用 Spring Initializr
  7. 使用官方 Initializr
  8. 2.2 手动创建 pom.xml
  9. 三、配置 MyBatis-Plus
  10. 3.1 应用配置(application.yml)
  11. Spring Boot 4.0 新配置格式
  12. 数据源配置(虚拟线程优化)
  13. AOT 编译配置
  14. GraalVM 原生镜像配置
  15. MyBatis-Plus 配置
  16. 日志配置
  17. 虚拟线程配置(Spring Boot 4.0 新特性)
  18. 3.2 MyBatis-Plus 配置类
  19. 四、实体类与 Mapper
  20. 4.1 基础实体类
  21. 4.2 用户实体类
  22. 4.3 Mapper 接口
  23. 五、Service 层实现
  24. 5.1 接口定义
  25. 5.2 服务实现
  26. 六、Controller 层
  27. 6.1 传统 REST Controller
  28. 6.2 响应式 Controller
  29. 七、响应式 Repository
  30. 八、全局异常处理
  31. 九、虚拟线程配置
  32. 十、AOT 和原生镜像支持
  33. 10.1 创建 AOT 处理类
  34. 10.2 native-image.properties
  35. 十一、测试类
  36. 十二、性能监控
  37. 十三、启动类
  38. 十四、部署和优化
  39. 14.1 Dockerfile(支持原生镜像)
  40. 构建阶段
  41. 运行阶段
  42. 14.2 Docker Compose
  43. 十五、总结
  44. 15.1 关键特性使用
  45. 15.2 性能优化建议
  46. 15.3 整合事项
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 文本生成技术:原理、落地场景与国产工具实践
  • 企业级农产品直卖平台:SpringBoot+Vue+MyBatis 架构
  • Ubuntu 安装 OpenClaw 并接入飞书机器人
  • 数据结构初阶详解线性表之队列
  • BloomFilter 原理及 Zset 实现原理
  • 前端SSE(Server-Sent Events)实现详解:从原理到前端AI对话应用
  • AtCoder Better 插件安装与 AI 翻译配置教程
  • HarmonyOS 6.0 Network Kit 深度解析:TLS 认证全面支持国密证书
  • 亲测一个月后,我把所有 AI 工具全卸载了:Megick 专业图像视频生成,接入 GPT-Image-2 和 Seedance 后直接封神
  • C++性能优化:提升代码执行效率的核心技巧
  • AI 时代的生产力重构:非技术背景者的产品化路径
  • 命令行大模型交互工具 MCPHost 实战指南
  • VSCode Cline 配置 ChatGPT Plus 与 GitHub Copilot 模型教程
  • Claude Code 提升效率的 Superpower 与记忆插件实战指南
  • 大模型时代:为何传统机器学习仍是 AI 入门最佳路径
  • Llama 3.1 开源模型快速部署教程
  • 美团大模型应用算法工程师岗位职责与技术栈详解
  • 软件工程范式演进:低代码技术逻辑与全栈化趋势
  • AI 智能编码工具深度测评:GitHub Copilot 与国产新秀对比
  • Python uv 工具:安装、升级与卸载指南

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online