Spring Boot 3 整合 SSM
场景进行数据访问
熟悉 SSM 框架
SSM 框架简介
SSM 框架是 Spring、Spring MVC 和 MyBatis 三个框架的整合,是 Java
企业级开发中经典的技术栈组合。其中:
Spring
提供了依赖注入(DI)和面向切面编程(AOP)等核心功能,用于管理对象生命周期和业务逻辑分层
Spring MVC 是基于 MVC 设计模式的 Web
框架,负责处理 Web 请求和响应
MyBatis 是持久层框架,用于实现数据库操作的 ORM
映射
Spring Boot 3 整合 SSM 的优势很明显
Spring Boot 3 对 SSM 框架的整合带来了以下核心优势:
自动化配置 :通过 Starter 依赖自动配置
Spring、Spring MVC 和 MyBatis 的基础环境
简化部署 :内置 Tomcat 等容器,可直接打包为可执行
JAR
性能优化 :基于 Spring 6 的响应式编程模型,支持
Reactive Stream
微服务支持 :天然适配 Spring Cloud
生态,便于构建微服务架构
SSM 各组件在场景中的角色
SpringBoot 整合 Spring 、SpringMVC 、MyBatis 进行数据访问场景开发
Spring
作为整个架构的基石,负责管理应用中的所有组件。它提供了两大核心功能:
依赖注入 (DI) :通过控制反转 (IoC)
实现组件间的松耦合
面向切面编程
(AOP) :用于处理事务、日志、安全等横切关注点
在 Spring Boot 3 中,不会再使用 Spring Framework 6
中复杂的配置,配置更加简化,通常使用 Java 注解声明为配置类代替 XML
配置:
1 2 3 4 5 6 7 8 9 @Configuration @EnableTransactionManagement public class AppConfig { @Bean public UserService userService () { return new UserServiceImpl (); } }
Spring MVC 控制器层负责处理 Web 请求,遵循 MVC 设计模式:
Model :业务数据和逻辑
View :用户界面
Controller :处理请求并协调 Model 和 View
负责接收前端请求并返回响应,通常与 RESTful 接口设计结合:
在 Spring Boot 3 中,RESTful API 开发更加便捷:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @RestController @RequestMapping("/api/users") public class UserController { @Autowired private UserService userService; @GetMapping public List<User> getAllUsers () { return userService.findAll(); } @PostMapping @ResponseStatus(HttpStatus.CREATED) public User createUser (@RequestBody User user) { return userService.save(user); } }
MyBatis 作为 ORM 框架,负责处理数据库操作:
SQL 映射 :通过 XML 或注解定义 SQL 语句
结果映射 :将查询结果映射到 Java 对象
参数映射 :将 Java 对象转换为 SQL 参数
在 Spring Boot 3 中,通常使用 Mapper 接口和注解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Mapper public interface UserMapper { @Select("SELECT * FROM users WHERE id = #{id}") User findById (Long id) ; @Insert("INSERT INTO users(name, email) VALUES(#{name}, #{email})") @Options(useGeneratedKeys = true, keyProperty = "id") void save (User user) ; @Update("UPDATE users SET name = #{name} WHERE id = #{id}") void update (User user) ; @Delete("DELETE FROM users WHERE id = #{id}") void delete (Long id) ; }
组件间协作流程
sequenceDiagram
Client->>Controller: HTTP 请求
Controller->>Service: 调用业务方法
Service->>Mapper: 调用数据访问方法
Mapper->>Database: 执行 SQL 操作
Database-->>Mapper: 返回结果
Mapper-->>Service: 返回数据对象
Service-->>Controller: 返回业务结果
Controller-->>Client: HTTP 响应
数据访问流程
整合 SSM 场景的 Spring Boot
程序
创建SSM整合项目
Spring Initializier部分
image-20250611172727771
image-20250611172833188
所以,需要导入的关键依赖如下
核心依赖
Spring 核心依赖
1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency >
作用 :提供 Spring MVC 框架支持,包括
DispatcherServlet、控制器、视图解析等功能
包含组件 :Spring MVC、Spring Web、Tomcat
嵌入式服务器
MyBatis 集成依赖
1 2 3 4 5 <dependency > <groupId > org.mybatis.spring.boot</groupId > <artifactId > mybatis-spring-boot-starter</artifactId > <version > 3.0.4</version > </dependency >
作用 :MyBatis 与 Spring Boot 的集成包,自动配置
MyBatis 环境
包含组件 :MyBatis 核心库、MyBatis-Spring 集成、SQL
会话工厂等
如果你不使用 MyBatis,Spring 的 JPA 依赖的实现会与 MyBatis
的基本功能重叠,在标准 SSM 架构中,通常使用 MyBatis 而非
JPA。若同时存在两者,需注意配置冲突
数据库连接依赖
1 2 3 4 5 6 7 8 9 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-jdbc</artifactId > </dependency > <dependency > <groupId > com.mysql</groupId > <artifactId > mysql-connector-j</artifactId > <scope > runtime</scope > </dependency >
spring-boot-starter-jdbc :提供 JDBC 抽象层,包含
HikariCP 连接池
mysql-connector-j :MySQL 数据库驱动,用于连接 MySQL
数据库
相关配置项
基本配置略过了
数据源相关配置
采用 MySQL 数据库,使用 HikariCP 连接池 (默认)
连接参数包含时区设置、字符编码等关键配置
连接池参数根据应用并发量可调整
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 spring.datasource.url =jdbc:mysql://localhost:3306/ssm_db?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=UTF-8 spring.datasource.username =root spring.datasource.password =root spring.datasource.driver-class-name =com.mysql.cj.jdbc.Driver spring.datasource.hikari.minimum-idle =5 spring.datasource.hikari.maximum-pool-size =15 spring.datasource.hikari.auto-commit =true spring.datasource.hikari.idle-timeout =30000 spring.datasource.hikari.pool-name =HikariCP spring.datasource.hikari.max-lifetime =1800000 spring.datasource.hikari.connection-timeout =30000 spring.datasource.hikari.connection-test-query =SELECT 1
连接池大小调优 :
过小会导致连接争用,影响性能
过大会占用过多资源,增加 GC 压力
计算公式:(核心数 * 2) + 磁盘数
作为参考值
安全性考虑 :
生产环境应使用更安全的密码管理方式(如配置中心、Vault 等)
考虑启用 SSL 连接(移除useSSL=false
参数)
性能优化 :
根据业务特性调整minimum-idle
和maximum-pool-size
对于读写比例高的应用,可考虑读写分离配置
监控与调优 :
结合 Actuator 监控连接池指标(活跃连接数、等待队列等)
根据监控数据动态调整连接池参数
MyBatis 配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 mybatis.mapper-locations =classpath:mapper/*.xml mybatis.type-aliases-package =com.example.entity mybatis.configuration.map-underscore-to-camel-case =true # 开启驼峰命名自动映射 mybatis.configuration.cache-enabled =true # 开启二级缓存 mybatis.configuration.lazy-loading-enabled =true # 开启懒加载 mybatis.configuration.aggressive-lazy-loading =false # 关闭激进懒加载 mybatis.configuration.default-fetch-size =100 # 默认获取记录数 mybatis.configuration.default-statement-timeout =30 # 默认SQL超时时间
安装 MyBatisX 插件,帮我们⽣成 Mapper 接⼝的 xml 文件即可
实体类相关编写
实体类就使用简单的 User 类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 package edu.software.ergoutree.springbootssmdataassess.entity;import lombok.Data;import java.io.Serializable;import java.util.Date;@Data public class User implements Serializable { private Long id; private String username; private String password; private String realName; private String email; private String phone; private Integer status; private Date createTime; private Date updateTime; private static final long serialVersionUID = 1L ; }
MyBatis 相关 mapper
首先,来复习一下 MyBatis,MyBatis 是一个半自动化的
ORM(对象关系映射)框架,核心功能是将 SQL 语句与 Java 对象进行映射。在
SSM 项目中,MyBatis 主要负责数据持久层的操作,其核心组件包括:
SqlSessionFactory :创建 SqlSession
的工厂,通过配置文件或代码构建
SqlSession :提供数据库操作的接口,如查询、更新、事务管理等
Mapper 接口 :定义数据库操作方法的接口
Mapper XML 文件 :实现 Mapper 接口方法的 SQL
映射配置
TypeHandler :处理 Java 类型与数据库类型的转换
ResultMap :定义复杂的结果集映射规则
所以,我们包含 CURD 操作的 UserMapper 就如下
那么,如何进行 CURD 的开发呢,主要开发流程如下
编写 Bean
编写 Mapper
使用插件或手写 MapperXML
编写 CURD 语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 package edu.software.ergoutree.springbootssmdataassess.mapper;import edu.software.ergoutree.springbootssmdataassess.entity.User;import org.apache.ibatis.annotations.Mapper;import org.apache.ibatis.annotations.Param;import java.util.List;@Mapper public interface UserMapper { int insert (User user) ; int deleteById (Long id) ; int update (User user) ; User selectById (Long id) ; List<User> selectAll () ; User selectByUsername (String username) ; List<User> selectByCondition (User user) ; int batchDelete (@Param("ids") List<Long> ids) ; int count () ; }
然后,我们就需要为 Mapper 编写对应的 xml 文件,在这里可以使用对应的
MyBatisX 插件自动生成
在这里复习以下 MyBatis 提供的 SQL 标签:
<where>
:自动处理 SQL 语句中的
WHERE 关键字和多余的 AND/OR
<if>
:根据条件判断是否包含某段
SQL
<>set
:自动处理 UPDATE 语句中的
SET 关键字和多余的逗号
<foreach>
:遍历集合参数,生成 IN
条件或批量操作
<choose>/<when>/<otherwise>
:类似
Java 的 switch-case 结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="edu.software.ergoutree.springbootssmdataassess.mapper.UserMapper" > <resultMap id ="BaseResultMap" type ="edu.software.ergoutree.springbootssmdataassess.entity.User" > <id column ="id" jdbcType ="BIGINT" property ="id" /> <result column ="username" jdbcType ="VARCHAR" property ="username" /> <result column ="password" jdbcType ="VARCHAR" property ="password" /> <result column ="real_name" jdbcType ="VARCHAR" property ="realName" /> <result column ="email" jdbcType ="VARCHAR" property ="email" /> <result column ="phone" jdbcType ="VARCHAR" property ="phone" /> <result column ="status" jdbcType ="INTEGER" property ="status" /> <result column ="create_time" jdbcType ="TIMESTAMP" property ="createTime" /> <result column ="update_time" jdbcType ="TIMESTAMP" property ="updateTime" /> </resultMap > <sql id ="Base_Column_List" > id, username, password, real_name, email, phone, status, create_time, update_time </sql > <insert id ="insert" parameterType ="edu.software.ergoutree.springbootssmdataassess.entity.User" useGeneratedKeys ="true" keyProperty ="id" > insert into user ( username, password, real_name, email, phone, status, create_time, update_time ) values ( #{username,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}, #{realName,jdbcType=VARCHAR}, #{email,jdbcType=VARCHAR}, #{phone,jdbcType=VARCHAR}, #{status,jdbcType=INTEGER}, now(), now() ) </insert > <delete id ="deleteById" parameterType ="java.lang.Long" > delete from user where id = #{id,jdbcType=BIGINT} </delete > <delete id ="batchDelete" parameterType ="java.util.List" > delete from user where id in <foreach collection ="ids" item ="id" open ="(" separator ="," close =")" > #{id} </foreach > </delete > <update id ="update" parameterType ="edu.software.ergoutree.springbootssmdataassess.entity.User" > update user <set > <if test ="username != null" > username = #{username,jdbcType=VARCHAR},</if > <if test ="password != null" > password = #{password,jdbcType=VARCHAR},</if > <if test ="realName != null" > real_name = #{realName,jdbcType=VARCHAR},</if > <if test ="email != null" > email = #{email,jdbcType=VARCHAR},</if > <if test ="phone != null" > phone = #{phone,jdbcType=VARCHAR},</if > <if test ="status != null" > status = #{status,jdbcType=INTEGER},</if > update_time = now() </set > where id = #{id,jdbcType=BIGINT} </update > <select id ="selectById" parameterType ="java.lang.Long" resultMap ="BaseResultMap" > select <include refid ="Base_Column_List" /> from user where id = #{id,jdbcType=BIGINT} </select > <select id ="selectAll" resultMap ="BaseResultMap" > select <include refid ="Base_Column_List" /> from user order by id desc </select > <select id ="selectByUsername" parameterType ="java.lang.String" resultMap ="BaseResultMap" > select <include refid ="Base_Column_List" /> from user where username = #{username,jdbcType=VARCHAR} </select > <select id ="selectByCondition" parameterType ="edu.software.ergoutree.springbootssmdataassess.entity.User" resultMap ="BaseResultMap" > select <include refid ="Base_Column_List" /> from user <where > <if test ="username != null and username != ''" > and username like concat('%', #{username}, '%') </if > <if test ="realName != null and realName != ''" > and real_name like concat('%', #{realName}, '%') </if > <if test ="email != null and email != ''" > and email = #{email} </if > <if test ="phone != null and phone != ''" > and phone = #{phone} </if > <if test ="status != null" > and status = #{status} </if > </where > order by id desc </select > <select id ="count" resultType ="java.lang.Integer" > select count(*) from user </select > </mapper >
之后就是对应的
Service 层 和 Controller 层
Service 接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 package edu.software.ergoutree.springbootssmdataassess.service;import edu.software.ergoutree.springbootssmdataassess.entity.User;import java.util.List;public interface UserService { boolean addUser (User user) ; boolean deleteUser (Long id) ; boolean updateUser (User user) ; User getUserById (Long id) ; List<User> getAllUsers () ; User getUserByUsername (String username) ; List<User> getUsersByCondition (User user) ; boolean batchDeleteUsers (List<Long> ids) ; int countUsers () ; }
实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 package edu.software.ergoutree.springbootssmdataassess.service.impl;import edu.software.ergoutree.springbootssmdataassess.entity.User;import edu.software.ergoutree.springbootssmdataassess.mapper.UserMapper;import edu.software.ergoutree.springbootssmdataassess.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;import java.util.List;@Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override @Transactional public boolean addUser (User user) { if (user.getStatus() == null ) { user.setStatus(1 ); } return userMapper.insert(user) > 0 ; } @Override @Transactional public boolean deleteUser (Long id) { return userMapper.deleteById(id) > 0 ; } @Override @Transactional public boolean updateUser (User user) { return userMapper.update(user) > 0 ; } @Override public User getUserById (Long id) { return userMapper.selectById(id); } @Override public List<User> getAllUsers () { return userMapper.selectAll(); } @Override public User getUserByUsername (String username) { return userMapper.selectByUsername(username); } @Override public List<User> getUsersByCondition (User user) { return userMapper.selectByCondition(user); } @Override @Transactional public boolean batchDeleteUsers (List<Long> ids) { return userMapper.batchDelete(ids) > 0 ; } @Override public int countUsers () { return userMapper.count(); } }
Controller 层的编写和之前没有什么差别
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 package edu.software.ergoutree.springbootssmdataassess.controller;import edu.software.ergoutree.springbootssmdataassess.entity.User;import edu.software.ergoutree.springbootssmdataassess.service.UserService;import edu.software.ergoutree.springbootssmdataassess.common.Result;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.*;import java.util.HashMap;import java.util.List;import java.util.Map;@RestController @RequestMapping("/users") public class UserController { @Autowired private UserService userService; @PostMapping public Result<User> createUser (@RequestBody User user) { boolean result = userService.addUser(user); if (result) { return Result.success("用户创建成功" , user); } else { return Result.error("用户创建失败" ); } } @GetMapping("/{id}") public Result<User> getUserById (@PathVariable Long id) { User user = userService.getUserById(id); if (user != null ) { return Result.success(user); } else { return Result.error(404 , "用户不存在" ); } } @GetMapping public Result<List<User>> getAllUsers () { List<User> users = userService.getAllUsers(); return Result.success(users); } @PutMapping("/{id}") public Result<Void> updateUser (@PathVariable Long id, @RequestBody User user) { user.setId(id); boolean result = userService.updateUser(user); if (result) { return Result.success("用户更新成功" , null ); } else { return Result.error("用户更新失败" ); } } @DeleteMapping("/{id}") public Result<Void> deleteUser (@PathVariable Long id) { boolean result = userService.deleteUser(id); if (result) { return Result.success("用户删除成功" , null ); } else { return Result.error("用户删除失败" ); } } @GetMapping("/by-username") public Result<User> getUserByUsername (@RequestParam String username) { User user = userService.getUserByUsername(username); if (user != null ) { return Result.success(user); } else { return Result.error(404 , "用户不存在" ); } } @PostMapping("/search") public Result<List<User>> searchUsers (@RequestBody User user) { List<User> users = userService.getUsersByCondition(user); return Result.success(users); } @DeleteMapping("/batch") public Result<Void> batchDeleteUsers (@RequestBody List<Long> ids) { boolean result = userService.batchDeleteUsers(ids); if (result) { return Result.success("批量删除成功" , null ); } else { return Result.error("批量删除失败" ); } } @GetMapping("/count") public Result<Integer> countUsers () { int count = userService.countUsers(); return Result.success(count); } }
启动类
启动类也要有相应改变
@MapperScan (basePackages = ““) mapper
文件的包位置,批量扫描注解,供扫描哪个包下的所有接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package edu.software.ergoutree.springbootssmdataassess;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@MapperScan(basePackages = "edu.software.ergoutree.springbootssmdataassess.mapper") @SpringBootApplication public class SpringBootSsmDataAssessApplication { public static void main (String[] args) { SpringApplication.run(SpringBootSsmDataAssessApplication.class, args); } }
配置类中也要配置每个接口的xml 的位置
1 mybatis.mapper-locations =classpath:mapper/*.xml
这样就会自动进行绑定
所以说,接口的全类名和namespace的值是一一对应的
测试发现接口都能正常使用获得信息
image-20250613105849633
SSM 整合总结
SSM 的核心整合的目标
就是基于 Spring Boot 3.5,通过 自动配置 简化传统 SSM
繁琐的 XML 配置,快速搭建 “控制层(SpringMVC)+
业务层(Spring)+ 持久层(MyBatis)” 三层架构,实现:
浏览器 / 前端 → SpringMVC(Controller)
接收请求
业务逻辑 → Spring(Service)
处理(含事务管理)
数据库操作 → MyBatis(Mapper) 执行 SQL
整合步骤如下:
依赖导入:用 Starter 简化配置
数据源配置:连接数据库
Spring Boot 会读取
application.yml
/application.properties
中的配置,自动创建数据源(默认用 HikariCP 连接池)。
持久层(MyBatis Mapper):数据库操作
MyBatis 负责执行 SQL,通过 Mapper 接口 + XML
映射文件 或 注解 实现。
首先先定义 Mapper 接口,创建 Mapper
接口,声明数据库操作方法(类似 DAO 层)
编写 XML 映射文件 ,在
resources/mapper/
下创建
UserMapper.xml
,编写复杂 SQL(与 Mapper
接口方法对应):
业务层(Spring Service):事务与逻辑
Service 层通过 @Service
被 Spring 管理,依赖 Mapper
操作数据库,并用 @Transactional
管理事务。
控制层(SpringMVC Controller):接收请求
Controller 负责接收前端请求,调用 Service 处理业务,返回响应(视图或
JSON)。
启动类:开启自动扫描
Spring Boot 3.5 中,启动类需标注
@SpringBootApplication
,自动扫描同包及子包的 Bean。
需要开启@MapperScan
:批量扫描 Mapper
接口包,无需每个接口加 @Mapper
,更简洁
SSM 自动配置原理和分析
Spring Boot 能简化 SSM 整合,核心依赖
“自动配置(Auto-Configuration)” 机制。其底层通过以下 3
个核心组件实现:
jdbc
场景的自动配置——DataSourceAutoConfiguration
这个部分配置了数据源等基本信息
mybatis-spring-boot-starter
会导入数据库的场景
spring-boot-starter-jdbc
,jdbc是操作数据库的场景
JDBC场景的几个自动配置如下,他们的自动配置使得Spring数据访问具有的底层能力:数据源、
JdbcTemplate 、事务
DataSourceAutoConfiguration
全类名:org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
核心作用 :自动创建数据库连接池(DataSource
),是所有数据库操作的基础。实现了数据源的自动配置
绑定配置 :将 application.yml
中
spring.datasource
前缀的配置(如
url
、username
、password
),绑定到
DataSourceProperties
类。
默认实现 :默认使用
HikariDataSource
(高性能连接池,Spring Boot 2.x+
起默认),替代传统的 Tomcat JDBC
或 DBCP
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 static class PooledDataSourceCondition extends AnyNestedCondition { PooledDataSourceCondition() { super (ConfigurationPhase.PARSE_CONFIGURATION); } @Conditional({PooledDataSourceAvailableCondition.class}) static class PooledDataSourceAvailable { PooledDataSourceAvailable() { } } @ConditionalOnProperty( prefix = "spring.datasource", name = {"type"} ) static class ExplicitType { ExplicitType() { } }
连接池优先级 :HikariCP > Tomcat JDBC > DBCP2
> Oracle UCP。
默认选择
HikariCP :若类路径中同时存在多个连接池,Spring Boot 会优先使用
HikariCP(性能最优)。
JdbcTemplateAutoConfiguration
全类名:org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration
,这个是负责JdbcTemplate
自动配置相关的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @AutoConfiguration( after = {DataSourceAutoConfiguration.class} ) @ConditionalOnClass({DataSource.class, JdbcTemplate.class}) @ConditionalOnSingleCandidate(DataSource.class) @EnableConfigurationProperties({JdbcProperties.class}) @Import({DatabaseInitializationDependencyConfigurer.class, JdbcTemplateConfiguration.class, NamedParameterJdbcTemplateConfiguration.class}) public class JdbcTemplateAutoConfiguration { public JdbcTemplateAutoConfiguration () { } }
DataSourceTransactionManagerAutoConfiguration
全类名:org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration
,这个是负责数据源事务管理器自动配置相关的
核心作用 :自动创建
DataSourceTransactionManager
(事务管理器),作为 Spring
声明式事务的基础支撑,与 @Transactional
注解协同,实现方法执行失败时的自动回滚等事务管理功能 ,为基于 JDBC
操作的数据库事务提供统一的管理机制。
使用场景 :在 Service 层方法上添加
@Transactional
,即可让方法执行失败时自动回滚:
自动配置条件:
1 2 3 4 5 6 7 8 9 @AutoConfiguration( before = {TransactionAutoConfiguration.class}, after = {DataSourceAutoConfiguration.class, TransactionManagerCustomizationAutoConfiguration.class} ) @ConditionalOnClass({DataSource.class, JdbcTemplate.class, TransactionManager.class}) @AutoConfigureOrder(Integer.MAX_VALUE) @EnableConfigurationProperties({DataSourceProperties.class}) public class DataSourceTransactionManagerAutoConfiguration {
内部配置类
JdbcTransactionManagerConfiguration
:
条件控制 :当容器中存在单一的
DataSource
Bean 时才会生效
创建事务管理器逻辑 :
transactionManager
方法:创建
DataSourceTransactionManager
Bean,先调用
createTransactionManager
方法构建基础事务管理器,再利用
ObjectProvider
对事务管理器进行自定义(若有自定义逻辑的话
)。
createTransactionManager
方法就是进行统一异常转换
DataSourceTransactionManager
(基础的数据源事务管理器实现
),最终都是构建出适配数据源的事务管理对象,用于管理数据库操作的事务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 static class JdbcTransactionManagerConfiguration { JdbcTransactionManagerConfiguration() { } @Bean @ConditionalOnMissingBean({TransactionManager.class}) DataSourceTransactionManager transactionManager (Environment environment, DataSource dataSource, ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) { DataSourceTransactionManager transactionManager = this .createTransactionManager(environment, dataSource); transactionManagerCustomizers.ifAvailable((customizers) -> { customizers.customize(transactionManager); }); return transactionManager; } private DataSourceTransactionManager createTransactionManager (Environment environment, DataSource dataSource) { return (DataSourceTransactionManager)((Boolean)environment.getProperty("spring.dao.exceptiontranslation.enabled" , Boolean.class, Boolean.TRUE) ? new JdbcTransactionManager (dataSource) : new DataSourceTransactionManager (dataSource)); } }
XADataSourceAutoConfiguration
(XA
数据源自动配置)
全类名:org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration
,基于XA⼆阶提交协议的分布式事务数据源
核心作用 :支持
分布式事务 (跨多个数据库 / 数据源的事务),基于 XA
协议(两阶段提交)保证多个数据源操作的原子性。
典型场景 :微服务中,一个业务操作需要同时更新
订单库
和 库存库
,用 XA
事务保证两者要么都成功,要么都回滚。
JndiDataSourceAutoConfiguration
(JNDI
数据源自动配置)
全类名:org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration
核心作用 :从 JNDI(Java Naming and Directory
Interface)获取数据源,适用于传统 Java EE 应用服务器(如
Tomcat、WebLogic)的场景。
配置了MyBatis的整合流程——MyBatisAutoConfiguration
mybatis-spring-boot-starter
会导入 mybatis
的自动配置包mybatis-spring-boot-autoconfigure
之后就会默认加载两个自动配置类
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
核心类:MyBatisAutoConfiguration
解析
全类名
org.mybatis.spring.boot.autoconfigure.MyBatisAutoConfiguration
核心作用
自动整合 MyBatis 到 Spring Boot 体系,替代传统 XML
配置(如
mybatis-config.xml
、SqlSessionFactoryBean
配置)。
自动创建
SqlSessionFactory
、SqlSessionTemplate
等核心组件,让开发者直接注入 Mapper
接口即可使用。
自动配置的触发条件
首先会进行依赖的导入
当项目引入mybatis-spring-boot-starter
时,会间接引入:
mybatis-spring-boot-autoconfigure
:包含 MyBatis
自动配置类(如 MyBatisAutoConfiguration
)。
mybatis-spring
:实现 MyBatis 与 Spring 的整合(如
SqlSessionFactoryBean
)。
自动配置通过如下条件注解决定是否生效
1 2 3 4 5 6 7 8 9 10 11 12 @Configuration( proxyBeanMethods = false ) @ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class}) @ConditionalOnSingleCandidate(DataSource.class) @EnableConfigurationProperties({MybatisProperties.class}) @AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class}) public class MybatisAutoConfiguration implements InitializingBean {
其中@ConditionalOnBean(DataSource.class)
:依赖
DataSourceAutoConfiguration
已创建的数据源。所以必须先创建数据源,并且数据源配置好之后才会生效,这和
Hibertnate 会自动创建数据表有一定的差别
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
会放入
MyBatis 和核心组件,session工厂相关的两个组件,分别是
给容器中放 SqlSessionFactory
组件。创建和数据库的⼀次会话
给容器中 SqlSessionTemplate
组件。操作数据库
MyBatis的所有配置绑定在
MybatisProperties
可以发现public class MybatisAutoConfiguration implements InitializingBean
中存在
1 @EnableConfigurationProperties({MybatisProperties.class})
所以,MybatisAutoConfiguration
自动配置需要启用MybatisProperties.class
,也就是说MyBatis
的所有配置绑定在
MybatisProperties
,我们来看看其中有什么内容
MybatisProperties
是 Spring Boot 整合 MyBatis
时的核心配置类,通过
@ConfigurationProperties(prefix = "mybatis")
绑定
application.yml
中 mybatis
前缀的配置。
1 2 3 @ConfigurationProperties( prefix = "mybatis" )
其中的一些基础配置项如表
配置项
类型
对应 application.yml
配置
作用描述
configLocation
String
mybatis.config-location
指定 MyBatis 全局配置文件路径(如
mybatis-config.xml
),优先级高于
application.yml
配置
mapperLocations
String[]
mybatis.mapper-locations
扫描 Mapper XML 文件路径(如
classpath:mapper/*.xml
),支持 Ant 风格路径
typeAliasesPackage
String
mybatis.type-aliases-package
扫描实体类包,自动注册类名作为别名(如 User
替代
com.example.User
)
typeAliasesSuperType
Class<?>
mybatis.type-aliases-super-type
仅注册指定父类的子类作为类型别名,缩小扫描范围
checkConfigLocation
boolean
mybatis.check-config-location
启动时检查配置文件是否存在,默认 false
对应的代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 public String getConfigLocation () { return this .configLocation; } public void setConfigLocation (String configLocation) { this .configLocation = configLocation; } public String[] getMapperLocations() { return this .mapperLocations; } public void setMapperLocations (String[] mapperLocations) { this .mapperLocations = mapperLocations; } public String getTypeHandlersPackage () { return this .typeHandlersPackage; } public void setTypeHandlersPackage (String typeHandlersPackage) { this .typeHandlersPackage = typeHandlersPackage; } public String getTypeAliasesPackage () { return this .typeAliasesPackage; } public void setTypeAliasesPackage (String typeAliasesPackage) { this .typeAliasesPackage = typeAliasesPackage; } public Class<?> getTypeAliasesSuperType() { return this .typeAliasesSuperType; } public void setTypeAliasesSuperType (Class<?> typeAliasesSuperType) { this .typeAliasesSuperType = typeAliasesSuperType; } public boolean isCheckConfigLocation () { return this .checkConfigLocation; } public void setCheckConfigLocation (boolean checkConfigLocation) { this .checkConfigLocation = checkConfigLocation; }
类型处理器与语言驱动配置
配置项
类型
对应配置
作用描述
typeHandlersPackage
String
mybatis.type-handlers-package
扫描类型处理器(TypeHandler
)包,自动注册自定义类型转换逻辑
defaultScriptingLanguageDriver
Class<? extends LanguageDriver>
mybatis.default-scripting-language-driver
指定默认脚本语言驱动(如
org.mybatis.scripting.xmltags.XMLLanguageDriver
)
源码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public String getTypeHandlersPackage () { return this .typeHandlersPackage; } public void setTypeHandlersPackage (String typeHandlersPackage) { this .typeHandlersPackage = typeHandlersPackage; } public Class<? extends LanguageDriver > getDefaultScriptingLanguageDriver() { return this .defaultScriptingLanguageDriver; } public void setDefaultScriptingLanguageDriver (Class<? extends LanguageDriver> defaultScriptingLanguageDriver) { this .defaultScriptingLanguageDriver = defaultScriptingLanguageDriver; }
执行器与事务配置
配置项
类型
对应配置
作用描述
executorType
ExecutorType
mybatis.executor-type
设置执行器类型:SIMPLE
(默认)、REUSE
(重用连接)、BATCH
(批量执行)
而其中,核心配置项便是CoreConfiguration
内部类 CoreConfiguration
封装了 MyBatis
Configuration
类的核心配置,对应
mybatis.configuration
前缀的配置,是 MyBatis
行为的核心控制项。这个类其中有很多内容,只挑出重要的进行讲解
映射与驼峰命名配置
配置项
类型
对应配置
作用描述
mapUnderscoreToCamelCase
Boolean
mybatis.configuration.map-underscore-to-camel-case
开启下划线转驼峰映射(如 user_name
→
userName
)
autoMappingBehavior
AutoMappingBehavior
mybatis.configuration.auto-mapping-behavior
自动映射行为:NONE
/PARTIAL
(默认)/FULL
autoMappingUnknownColumnBehavior
AutoMappingUnknownColumnBehavior
mybatis.configuration.auto-mapping-unknown-column-behavior
未知列映射策略:NONE
(忽略)/WARNING
(警告)/FAILING
(报错)
示例 :
1 2 3 4 mybatis: configuration: map-underscore-to-camel-case: true auto-mapping-behavior: FULL
2. 延迟加载与缓存配置
配置项
类型
对应配置
作用描述
lazyLoadingEnabled
Boolean
mybatis.configuration.lazy-loading-enabled
启用延迟加载(按需加载关联对象)
aggressiveLazyLoading
Boolean
mybatis.configuration.aggressive-lazy-loading
激进延迟加载(加载一个属性时加载所有关联属性)
cacheEnabled
Boolean
mybatis.configuration.cache-enabled
启用二级缓存(基于命名空间的缓存)
localCacheScope
LocalCacheScope
mybatis.configuration.local-cache-scope
本地缓存作用域:SESSION
(默认)/STATEMENT
3. 执行与超时配置
配置项
类型
对应配置
作用描述
defaultStatementTimeout
Integer
mybatis.configuration.default-statement-timeout
SQL 执行超时时间(秒)
defaultFetchSize
Integer
mybatis.configuration.default-fetch-size
结果集批量获取行数(优化大数据查询)
useGeneratedKeys
Boolean
mybatis.configuration.use-generated-keys
启用自增主键获取(如 MySQL 的 AUTO_INCREMENT
)
4. 日志与 VFS 配置
配置项
类型
对应配置
作用描述
logImpl
Class<? extends Log>
mybatis.configuration.log-impl
指定日志实现(如
org.apache.ibatis.logging.stdout.StdOutImpl
)
vfsImpl
Class<? extends VFS>
mybatis.configuration.vfs-impl
其中,MybatisProperties
提供
resolveMapperLocations()
方法解析
mapperLocations
配置,支持 Ant 风格路径(如
classpath*:mapper/**/*.xml
),内部通过
PathMatchingResourcePatternResolver
实现资源扫描。
1 2 3 4 5 public Resource[] resolveMapperLocations() { return Stream.of(Optional.ofNullable(mapperLocations).orElse(new String [0 ])) .flatMap(location -> Stream.of(getResources(location))) .toArray(Resource[]::new ); }
configLocation
指定的 XML 配置 >
application.yml
中 mybatis.configuration
配置
> MyBatis 默认值。
自动配置流程 :
Spring Boot 启动时,MyBatisAutoConfiguration
读取
MybatisProperties
配置。
通过 CoreConfiguration.applyTo(Configuration)
方法将配置应用到 MyBatis 的 Configuration
对象。
通过 MybatisProperties
,Spring Boot 实现了 MyBatis
配置的全量绑定,开发者无需编写 XML 配置文件,仅通过
application.yml
即可完成 MyBatis 的所有核心配置,真正实现
“约定优于配置” 的开发体验。
每个Mapper接⼝的代理对象是怎么创建放到容器中——@MapperScan原理
MyBatis 中 Mapper
是接口 ,本身无法直接创建对象。@MapperScan
的作用是: 通过 动态代理 + Spring Bean 注册 ,让每个
Mapper 接口生成代理对象 (能执行 SQL),并注入到 Spring
容器,最终实现 @Autowired UserMapper userMapper
直接用。
sequenceDiagram
participant 启动类 as Spring Boot 启动类(@MapperScan)
participant ImportRegistrar as MapperScannerRegistrar(@Import 引入)
participant ScannerConfigurer as MapperScannerConfigurer(注册 BeanDefinition)
participant ClassPathMapperScanner as ClassPathMapperScanner(扫描 Mapper)
participant MapperFactoryBean as MapperFactoryBean(创建代理对象)
启动类->>ImportRegistrar: 加载 @MapperScan,触发 Import
ImportRegistrar->>ScannerConfigurer: 注册 MapperScannerConfigurer 的 BeanDefinition
ScannerConfigurer->>ClassPathMapperScanner: 扫描指定包(如 com.example.mapper)
ClassPathMapperScanner->>MapperFactoryBean: 为每个 Mapper 接口创建 BeanDefinition
MapperFactoryBean->>Spring容器: 生成 Mapper 代理对象,注册到容器
MapperScan 接口的源码就是这样
@MapperScan
注解的触发入口如下,其中@Import(MapperScannerRegistrar.class)
引入逻辑处理类,替代手动配置
MapperScannerConfigurer
是核心。
1 2 3 4 5 6 7 8 9 10 11 @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented @Import({MapperScannerRegistrar.class}) @Repeatable(MapperScans.class) public @interface MapperScan { @AliasFor("basePackages") String[] value() default {}; @AliasFor("value") String[] basePackages() default {};
而解析 @MapperScan
配置的类就是MapperScannerRegistrar
,它的作用就是实现
ImportBeanDefinitionRegistrar
接口,动态注册
MapperScannerConfigurer
到 Spring 容器。
也就是将 @MapperScan
的配置(如扫描包路径)传递给
MapperScannerConfigurer
,将 @MapperScan
注解的配置转换为 Spring 容器中的 Mapper Bean。
MapperScannerRegistrar
源码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar , ResourceLoaderAware { private ResourceLoader resourceLoader; public MapperScannerRegistrar () { } public void setResourceLoader (ResourceLoader resourceLoader) { this .resourceLoader = resourceLoader; } public void registerBeanDefinitions (AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AnnotationAttributes mapperScanAttrs = AnnotationAttributes.fromMap( importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName())); if (mapperScanAttrs != null ) { this .registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0 )); } }
其中,上述registerBeanDefinitions
方法,做到了解析
@MapperScan
注解(如
@MapperScan(basePackages = "com.example.mapper")
),并将配置传递给
MapperScannerConfigurer
。
配置 MapperScannerConfigurer
的
BeanDefinition
的源码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 void registerBeanDefinitions (AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class); builder.addPropertyValue("processPropertyPlaceHolders" , annoAttrs.getBoolean("processPropertyPlaceHolders" )); Class<? extends Annotation > annotationClass = annoAttrs.getClass("annotationClass" ); if (!Annotation.class.equals(annotationClass)) { builder.addPropertyValue("annotationClass" , annotationClass); } Class<?> markerInterface = annoAttrs.getClass("markerInterface" ); if (!Class.class.equals(markerInterface)) { builder.addPropertyValue("markerInterface" , markerInterface); } Class<? extends BeanNameGenerator > generatorClass = annoAttrs.getClass("nameGenerator" ); if (!BeanNameGenerator.class.equals(generatorClass)) { builder.addPropertyValue("nameGenerator" , BeanUtils.instantiateClass(generatorClass)); } Class<? extends MapperFactoryBean > mapperFactoryBeanClass = annoAttrs.getClass("factoryBean" ); if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) { builder.addPropertyValue("mapperFactoryBeanClass" , mapperFactoryBeanClass); } String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef" ); if (StringUtils.hasText(sqlSessionTemplateRef)) { builder.addPropertyValue("sqlSessionTemplateBeanName" , sqlSessionTemplateRef); } String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef" ); if (StringUtils.hasText(sqlSessionFactoryRef)) { builder.addPropertyValue("sqlSessionFactoryBeanName" , sqlSessionFactoryRef); } List<String> basePackages = new ArrayList <>(); basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages" )) .filter(StringUtils::hasText) .collect(Collectors.toList())); basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses" )) .map(ClassUtils::getPackageName) .collect(Collectors.toList())); if (basePackages.isEmpty()) { basePackages.add(getDefaultBasePackage(annoMeta)); } AnnotationAttributes[] excludeFilterArray = annoAttrs.getAnnotationArray("excludeFilters" ); if (excludeFilterArray.length > 0 ) { List<TypeFilter> typeFilters = new ArrayList <>(); List<Map<String, String>> rawTypeFilters = new ArrayList <>(); builder.addPropertyValue("excludeFilters" , typeFilters); builder.addPropertyValue("rawExcludeFilters" , rawTypeFilters); } String lazyInitialization = annoAttrs.getString("lazyInitialization" ); if (StringUtils.hasText(lazyInitialization)) { builder.addPropertyValue("lazyInitialization" , lazyInitialization); } String defaultScope = annoAttrs.getString("defaultScope" ); if (!"" .equals(defaultScope)) { builder.addPropertyValue("defaultScope" , defaultScope); } builder.addPropertyValue("basePackage" , StringUtils.collectionToCommaDelimitedString(basePackages)); builder.setRole(2 ); registry.registerBeanDefinition(beanName, builder.getBeanDefinition()); }
上述源码的核心功能就是将 @MapperScan
的各种配置(如
basePackages
、annotationClass
、excludeFilters
)转换为
MapperScannerConfigurer
的属性,最终实现注册到
Spring 容器 。
下面的就是一些过滤器和重复注解处理,就不看了
总之,MapperScannerRegistrar
的核心流程就是
解析注解 :从 @MapperScan
获取配置(如扫描包路径、过滤器)。
创建配置器 :创建
MapperScannerConfigurer
的
BeanDefinition,并设置属性。
注册配置器 :将 MapperScannerConfigurer
注册到 Spring 容器。
延迟扫描 :MapperScannerConfigurer
在
Spring 容器启动后,才会真正扫描 Mapper 接口并生成代理对象。
也即是说,MapperScan
接口使用了@Import({MapperScannerRegistrar.class})
导入了Mapper注册成了bean对象,其中,也做到了批量给容器中注册组件。解析指定的包路径里面的每⼀个类,为每⼀个Mapper接口类,创建Bean定义信息,注册到容器中。
还有一个类能扫描 Mapper
接口MapperScannerConfigurer
,实现
BeanDefinitionRegistryPostProcessor
接口,在 Spring
启动早期扫描 Mapper 接口
其中比较核心的代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public void postProcessBeanDefinitionRegistry (BeanDefinitionRegistry registry) { if (this .processPropertyPlaceHolders) { this .processPropertyPlaceHolders(); } ClassPathMapperScanner scanner = new ClassPathMapperScanner (registry, this .getEnvironment()); scanner.setAddToConfig(this .addToConfig); scanner.setAnnotationClass(this .annotationClass); scanner.setMarkerInterface(this .markerInterface); scanner.setExcludeFilters(this .excludeFilters = this .mergeExcludeFilters()); scanner.setSqlSessionFactory(this .sqlSessionFactory); scanner.setSqlSessionTemplate(this .sqlSessionTemplate); scanner.setSqlSessionFactoryBeanName(this .sqlSessionFactoryBeanName); scanner.setSqlSessionTemplateBeanName(this .sqlSessionTemplateBeanName); scanner.setResourceLoader(this .applicationContext); scanner.setBeanNameGenerator(this .nameGenerator); scanner.setMapperFactoryBeanClass(this .mapperFactoryBeanClass); if (StringUtils.hasText(this .lazyInitialization)) { scanner.setLazyInitialization(Boolean.parseBoolean(this .lazyInitialization)); } if (StringUtils.hasText(this .defaultScope)) { scanner.setDefaultScope(this .defaultScope); } scanner.registerFilters(); scanner.scan(StringUtils.tokenizeToStringArray(this .basePackage, ",; \t\n" )); }
其中,ClassPathMapperScanner
是 MyBatis
扩展的扫描器,专门处理 Mapper 接口。
而ClassPathMapperScanner
就是生成 Mapper
的
BeanDefinition
,它继承
ClassPathBeanDefinitionScanner
,重写
doScan
方法 ,为每个 Mapper 接口创建
BeanDefinition
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner { public Set<BeanDefinitionHolder> doScan (String... basePackages) { Set<BeanDefinitionHolder> beanDefinitions = super .doScan(basePackages); if (beanDefinitions.isEmpty()) { if (this .printWarnLogIfNotFoundMappers) { LOGGER.warn(() -> { return "No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration." ; }); } } else { this .processBeanDefinitions(beanDefinitions); } return beanDefinitions; } }
之后MapperFactoryBean
的作用就是创建 Mapper
代理对象,继承 FactoryBean
,动态生成 Mapper
接口的代理对象 :
1 2 3 4 5 6 7 8 9 10 public class MapperFactoryBean <T> extends SqlSessionDaoSupport implements FactoryBean <T> { private Class<T> mapperInterface; public T getObject () throws Exception { return this .getSqlSession().getMapper(this .mapperInterface); } public Class<T> getObjectType () { return this .mapperInterface; }
sqlSession.getMapper(mapperInterface)
会通过 JDK
动态代理,为 Mapper 接口生成代理对象 ,代理对象内部通过
SqlSession
执行 SQL。
所以说从 @MapperScan
到 Mapper
可用的完整流程总结如下:
启动触发 :@MapperScan
通过
@Import
引入 MapperScannerRegistrar
。
注册配置类 :MapperScannerRegistrar
向
Spring 容器注册 MapperScannerConfigurer
。
扫描 Mapper :MapperScannerConfigurer
启动扫描,找到所有 Mapper 接口。
替换 BeanClass :将 Mapper 接口的
BeanClass
替换为 MapperFactoryBean
。
生成代理对象 :MapperFactoryBean
利用
MyBatis 的 SqlSession
,为 Mapper
接口生成动态代理对象 ,并注册到 Spring 容器。
最终,你可以通过 @Autowired UserMapper userMapper
直接注入 Mapper 代理对象,执行 SQL 时由代理对象转发给 MyBatis 执行。
如何找自动配置类
如何分析哪个场景导⼊以后,开启了哪些自动配置类。
找:
classpath:/META-INF/spring/org.springframework.boot.autoconfigure. AutoConfiguration.imports
文件中配置的所有值,就是要开启的⾃动配置类,但是每个
类可能有条件注解,基于条件注解判断哪个⾃动配置类⽣效了
快速定位⽣效的配置可以开启如下
整合其他数据源
Druid 数据源
暂不⽀持 SpringBoot3
导入 druid-starter
写配置
分析自动配置了哪些东⻄,怎么用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 spring.datasource.url =jdbc:mysql://192.168.200.100:3306/demo spring.datasource.driver-class-name =com.mysql.cj.jdbc.Driver spring.datasource.username =root spring.datasource.password =123456 spring.datasource.type =com.alibaba.druid.pool.DruidDataSource spring.datasource.druid.filter.stat.enabled =true spring.datasource.druid.filter.stat.db-type =mysql spring.datasource.druid.filter.stat.log-slow-sql =true spring.datasource.druid.filter.stat.slow-sql-millis =2000 spring.datasource.druid.filter.wall.enabled =true spring.datasource.druid.filter.wall.db-type =mysql spring.datasource.druid.filter.wall.config.delete-allow =false spring.datasource.druid.filter.wall.config.drop-table-allow =false spring.datasource.druid.stat-view-servlet.enabled =true spring.datasource.druid.stat-view-servlet.login-username =admin spring.datasource.druid.stat-view-servlet.login-password =admin spring.datasource.druid.stat-view-servlet.allow =*