part1-mybatis-autoconfigure
约 3919 字大约 13 分钟
2026-05-24
系列文章:《深入Spring事务与数据源:从注解到数据库连接的全链路解析》
系列三第1篇:MyBatis自动装配与SqlSessionFactory创建完整源码解析
基于版本:MyBatis 3.5.x、MyBatis-Spring 2.1.x、Spring Boot 2.7.x
前置阅读:系列二第5篇:连接有效性检测与性能调优
前言
系列二咱们把Druid连接池翻了个底朝天,从架构设计到连接管理,从Filter链到性能调优,全都搞清楚了。
但是,咱们一直有个问题没深入——MyBatis是怎么使用这些连接的?
你可能每天都在用MyBatis,写Mapper接口,写XML映射文件,然后调用userMapper.selectById(1),数据就查出来了。
但是你有没有想过:
- MyBatis是怎么和Spring整合的?
- SqlSessionFactory是怎么创建的?
- Mapper接口没有实现类,为什么能调用方法?
- MyBatis是怎么封装JDBC的?
别急,这个系列咱们就来把MyBatis的执行器源码整个扒一遍。而第一篇文章,咱们得从最基础的讲起——MyBatis的自动装配和SqlSessionFactory的创建。
而且我敢说,搞懂了SqlSessionFactory的创建流程,你对MyBatis的理解就能超过70%的Java开发!
好了,坐稳了,咱们开始!
一、回顾一下:Spring Boot是怎么自动装配MyBatis的?
老规矩,先看看自动装配的入口。这是咱们系列的传统,对吧?
核心入口1:MyBatis自动配置类
文件位置:mybatis-spring-boot-autoconfigure/src/main/java/org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.java
@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class }) // 检查classpath
@ConditionalOnSingleCandidate(DataSource.class) // 必须有且仅有一个DataSource
@AutoConfigureAfter(DataSourceAutoConfiguration.class) // 在数据源自动配置之后
@EnableConfigurationProperties(MybatisProperties.class) // 绑定MyBatis配置属性
public class MybatisAutoConfiguration implements InitializationBean {
private final MybatisProperties properties;
private final Interceptor[] interceptors;
private final TypeHandler[] typeHandlers;
private final LanguageDriver[] languageDrivers;
private final ResourceLoader resourceLoader;
public MybatisAutoConfiguration(MybatisProperties properties,
ObjectProvider<Interceptor[]> interceptorsProvider,
ObjectProvider<TypeHandler[]> typeHandlersProvider,
ObjectProvider<LanguageDriver[]> languageDriversProvider,
ResourceLoader resourceLoader) {
this.properties = properties;
this.interceptors = interceptorsProvider.getIfAvailable();
this.typeHandlers = typeHandlersProvider.getIfAvailable();
this.languageDrivers = languageDriversProvider.getIfAvailable();
this.resourceLoader = resourceLoader;
}
@PostConstruct
public void checkConfigFileExists() {
// 检查mybatis配置文件是否存在
if (this.properties.isCheckConfigLocation()
&& this.properties.getConfigLocation() != null) {
Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());
Assert.state(resource.exists(),
"Cannot find config location: " + resource + " (please add config file or check your Mybatis configuration)");
}
}
@Bean
@ConditionalOnMissingBean // 如果没有手动配置
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
// ★★★★★ 核心:创建SqlSessionFactory ★★★★★
return createSqlSessionFactory(dataSource);
}
// ... 其他Bean
}看到这个配置类,你可能会说:"就这?"
但是,请注意几个关键点:
@ConditionalOnSingleCandidate(DataSource.class):必须有DataSource,这说明MyBatis依赖数据源@AutoConfigureAfter(DataSourceAutoConfiguration.class):在数据源配置之后执行sqlSessionFactory()方法:这就是创建SqlSessionFactory的地方
核心入口2:创建SqlSessionFactory
文件位置:mybatis-spring-boot-autoconfigure/MybatisAutoConfiguration.java
private SqlSessionFactory createSqlSessionFactory(DataSource dataSource) throws Exception {
// ★★★★★ 核心:使用SqlSessionFactoryBean创建 ★★★★★
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
// 设置数据源
factoryBean.setDataSource(dataSource);
// 设置MyBatis配置文件
if (this.properties.getConfigLocation() != null) {
factoryBean.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
}
// 设置Mapper XML文件位置
if (this.properties.getMapperLocations() != null) {
factoryBean.setMapperLocations(this.properties.getMapperLocations());
}
// 设置类型处理器
if (this.typeHandlers != null) {
factoryBean.setTypeHandlers(this.typeHandlers);
}
// 设置拦截器(插件)
if (this.interceptors != null) {
factoryBean.setPlugins(this.interceptors);
}
// 设置配置属性
if (this.properties.getConfigurationProperties() != null) {
factoryBean.setConfigurationProperties(this.properties.getConfigurationProperties());
}
// ★★★★★ 核心:调用getObject()获取SqlSessionFactory ★★★★★
return factoryBean.getObject();
}看到没?核心就是SqlSessionFactoryBean这个类!
扩展场景1:MyBatis的starter依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.0</version>
</dependency>这个starter包含了:
mybatis:MyBatis核心mybatis-spring:MyBatis与Spring整合mybatis-spring-boot-autoconfigure:Spring Boot自动配置
扩展场景2:MyBatis的配置属性
mybatis:
config-location: classpath:mybatis-config.xml # MyBatis配置文件
mapper-locations: classpath:mapper/*.xml # Mapper XML位置
type-aliases-package: com.example.entity # 类型别名包
configuration:
map-underscore-to-camel-case: true # 下划线转驼峰
cache-enabled: false # 关闭二级缓存
lazy-loading-enabled: true # 开启懒加载好了,自动装配的部分搞清楚了,现在正式进入SqlSessionFactoryBean的源码!
二、SqlSessionFactoryBean:工厂Bean的魔法
你可能会问:"为什么要用SqlSessionFactoryBean,而不是直接 ew SqlSessionFactoryBuilder().build()`?"
这是因为Spring的FactoryBean机制!
核心机制:FactoryBean接口
文件位置:spring-beans/FactoryBean.java
package org.springframework.beans.factory;
/**
* FactoryBean接口
* 这是Spring提供的工厂Bean机制
*
* 普通Bean:容器返回的就是Bean本身
* FactoryBean:容器返回的是getObject()方法的返回值
*/
public interface FactoryBean<T> {
/**
* 返回创建的实例
*/
T getObject() throws Exception;
/**
* 返回实例的类型
*/
Class<?> getObjectType();
/**
* 是否是单例
*/
default boolean isSingleton() {
return true;
}
}看到了吧?FactoryBean的作用就是创建一个复杂的对象。
// 普通Bean
@Bean
public UserService userService() {
return new UserService(); // 容器返回UserService实例
}
// FactoryBean
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean() {
return new SqlSessionFactoryBean(); // 容器返回getObject()的返回值
}
// 当你注入时:
@Autowired
SqlSessionFactory sqlSessionFactory; // 实际是SqlSessionFactoryBean.getObject()的返回值所以,SqlSessionFactoryBean就是一个工厂,用来创建SqlSessionFactory。
三、SqlSessionFactoryBean.getObject():创建SqlSessionFactory的核心方法
好,现在来看getObject()方法,这是创建SqlSessionFactory的核心:
核心方法1:getObject
文件位置:mybatis-spring/SqlSessionFactoryBean.java
package org.mybatis.spring;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import javax.sql.DataSource;
/**
* SqlSessionFactoryBean
* 用于创建SqlSessionFactory的FactoryBean
*/
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean {
// 数据源
private DataSource dataSource;
// MyBatis配置文件位置
private Resource configLocation;
// Mapper XML文件位置
private Resource[] mapperLocations;
// 类型处理器
private TypeHandler<?>[] typeHandlers;
// 拦截器(插件)
private Interceptor[] plugins;
// 配置属性
private Properties configurationProperties;
// SqlSessionFactory实例(缓存)
private SqlSessionFactory sqlSessionFactory;
/**
* 返回SqlSessionFactory实例
*/
@Override
public SqlSessionFactory getObject() throws Exception {
// 如果已经创建过,直接返回
if (this.sqlSessionFactory == null) {
// ★★★★★ 核心:调用afterPropertiesSet()创建 ★★★★★
afterPropertiesSet();
}
return this.sqlSessionFactory;
}
/**
* 属性设置完成后调用(InitializingBean接口)
*/
@Override
public void afterPropertiesSet() throws Exception {
// ★★★★★ 核心:创建SqlSessionFactory ★★★★★
this.sqlSessionFactory = buildSqlSessionFactory();
}
/**
* 构建SqlSessionFactory(核心方法)
*/
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
// ... 下一节详细讲
}
}看到了吧?getObject()方法很简单:
- 检查是否已经创建(如果创建过,直接返回)
- 调用
afterPropertiesSet()(InitializingBean接口的方法) afterPropertiesSet()调用buildSqlSessionFactory()
这就是Spring Bean的生命周期:属性设置完成后,调用afterPropertiesSet()初始化。
扩展场景3:Spring Bean的生命周期
1. 实例化Bean(调用构造方法)
2. 设置属性(调用Setter方法)
3. 调用Aware接口方法(如BeanNameAware)
4. 调用BeanPostProcessor.beforeInitialization()
5. 调用InitializingBean.afterPropertiesSet() ← 这里!
6. 调用自定义init-method
7. 调用BeanPostProcessor.afterInitialization()
8. Bean可以使用了四、buildSqlSessionFactory:构建SqlSessionFactory的完整流程
好了,现在来看最核心的方法——buildSqlSessionFactory():
核心方法2:buildSqlSessionFactory
文件位置:mybatis-spring/SqlSessionFactoryBean.java
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
// ========== 第一步:创建Configuration对象 ==========
// Configuration是MyBatis的核心配置类
// 包含了所有的配置信息
Configuration configuration;
// 如果有MyBatis配置文件,解析配置文件
if (this.configLocation != null) {
// 解析mybatis-config.xml
configuration = new XMLConfigBuilder(
this.configLocation.getInputStream(), null, this.configurationProperties).parse();
} else {
// 如果没有配置文件,创建默认配置
configuration = new Configuration(this.configurationProperties);
}
// ========== 第二步:设置ObjectWrapperFactory ==========
if (this.objectWrapperFactory != null) {
configuration.setObjectWrapperFactory(this.objectWrapperFactory);
}
// ========== 第三步:设置VFS ==========
if (this.vfs != null) {
configuration.setVfsImpl(this.vfs);
}
// ========== 第四步:设置类型别名 ==========
if (hasLength(this.typeAliasesPackage)) {
// 扫描指定包下的类,注册类型别名
scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream()
.filter(clazz -> !clazz.isAnonymousClass())
.filter(clazz -> !clazz.isInterface())
.filter(clazz -> !clazz.isMemberClass())
.forEach(clazz -> {
// 注册别名
configuration.getTypeAliasRegistry().registerAlias(clazz);
});
}
// ========== 第五步:设置类型处理器 ==========
if (this.typeHandlers != null) {
for (TypeHandler<?> typeHandler : this.typeHandlers) {
// 注册类型处理器
configuration.getTypeHandlerRegistry().register(typeHandler);
}
}
// ========== 第六步:设置插件(拦截器)==========
if (this.plugins != null) {
for (Interceptor plugin : this.plugins) {
// 添加插件
configuration.addInterceptor(plugin);
}
}
// ========== 第七步:设置其他配置 ==========
if (hasLength(this.typeHandlersPackage)) {
// 扫描类型处理器包
scanClasses(this.typeHandlersPackage, TypeHandler.class).stream()
.filter(clazz -> !clazz.isAnonymousClass())
.filter(clazz -> !clazz.isInterface())
.filter(clazz -> !clazz.isMemberClass())
.forEach(clazz -> {
configuration.getTypeHandlerRegistry().register(clazz);
});
}
// ========== 第八步:解析Mapper XML文件 ==========
if (this.mapperLocations != null) {
for (Resource mapperLocation : this.mapperLocations) {
// 解析每个Mapper XML文件
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(
mapperLocation.getInputStream(),
configuration,
mapperLocation.toString(),
configuration.getSqlFragments());
// ★★★★★ 核心:解析Mapper XML ★★★★★
xmlMapperBuilder.parse();
}
}
// ========== 第九步:构建SqlSessionFactory ==========
// ★★★★★ 核心:使用SqlSessionFactoryBuilder构建 ★★★★★
return this.sqlSessionFactoryBuilder.build(configuration);
}这个方法做了9件事:
- 创建Configuration对象(核心配置类)
- 设置ObjectWrapperFactory
- 设置VFS
- 设置类型别名(扫描包)
- 设置类型处理器
- 设置插件(拦截器)
- 扫描类型处理器包
- 解析Mapper XML文件
- 构建SqlSessionFactory
关键点:最核心的就是第8步和第9步!
五、XMLMapperBuilder.parse():解析Mapper XML
好,现在来看第8步——解析Mapper XML文件。
核心方法3:parse
文件位置:mybatis-3/XMLMapperBuilder.java
package org.apache.ibatis.builder.xml;
import org.apache.ibatis.builder.BaseBuilder;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.apache.ibatis.session.Configuration;
import java.io.InputStream;
/**
* XML Mapper构建器
* 负责解析Mapper XML文件
*/
public class XMLMapperBuilder extends BaseBuilder {
private final XPathParser parser;
private final MapperBuilderAssistant builderAssistant;
private final Map<String, XNode> sqlFragments;
public XMLMapperBuilder(InputStream inputStream, Configuration configuration,
String resource, Map<String, XNode> sqlFragments) {
super(configuration);
this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
this.parser = new XPathParser(inputStream, true, configuration.getVariables(),
new XMLMapperEntityResolver());
this.sqlFragments = sqlFragments;
}
/**
* 解析Mapper XML
*/
public void parse() {
// 如果这个resource还没有被加载过
if (!configuration.isResourceLoaded(resource)) {
// ★★★★★ 核心:解析<mapper>节点 ★★★★★
configurationElement(parser.evalNode("/mapper"));
// 标记为已加载
configuration.addLoadedResource(resource);
// ★★★★★ 核心:绑定Mapper接口 ★★★★★
bindMapperForNamespace();
}
// 处理未完成的ResultMap和Statement
parsePendingResultMaps();
parsePendingStatements();
}
/**
* 解析<mapper>节点
*/
private void configurationElement(XNode context) {
try {
// ========== 第一步:获取namespace ==========
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.isEmpty()) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
// ========== 第二步:解析<cache>节点(二级缓存)==========
cacheElement(context.evalNode("cache"));
// ========== 第三步:解析<parameterMap>节点 ==========
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
// ========== 第四步:解析<resultMap>节点 ==========
resultMapElements(context.evalNodes("/mapper/resultMap"));
// ========== 第五步:解析<sql>节点 ==========
sqlElement(context.evalNodes("/mapper/sql"));
// ========== 第六步:解析SQL语句节点 ==========
// ★★★★★ 核心:解析<select>、<insert>、<update>、<delete> ★★★★★
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
}
}
}这个方法做了两件事:
- 解析Mapper XML(
configurationElement()) - 绑定Mapper接口(
bindMapperForNamespace())
关键点:configurationElement()方法会解析所有的XML节点,包括:
<cache>:二级缓存配置<resultMap>:结果映射<sql>:SQL片段<select>、<insert>、<update>、<delete>:SQL语句
六、buildStatementFromContext:解析SQL语句
好,现在来看最核心的部分——解析SQL语句。
核心方法4:buildStatementFromContext
文件位置:mybatis-3/XMLMapperBuilder.java
private void buildStatementFromContext(List<XNode> list) {
// 如果指定了数据库ID,就按数据库ID解析
if (configuration.getDatabaseId() != null) {
buildStatementFromContext(list, configuration.getDatabaseId());
}
// ★★★★★ 核心:创建XMLStatementBuilder解析 ★★★★★
XMLStatementBuilder statementParser = new XMLStatementBuilder(
configuration, builderAssistant, list, configuration.getDatabaseId());
try {
// 解析所有SQL语句
statementParser.parseStatementNode();
} catch (IncompleteElementException e) {
configuration.addIncompleteStatement(statementParser);
}
}这里委托给XMLStatementBuilder来解析SQL语句。
核心方法5:parseStatementNode
文件位置:mybatis-3/XMLStatementBuilder.java
public void parseStatementNode() {
// ========== 第一步:获取SQL属性 ==========
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
// 检查是否匹配当前数据库
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
// ========== 第二步:获取SQL类型 ==========
String nodeName = context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase());
// ========== 第三步:获取SQL配置 ==========
Integer fetchSize = context.getIntAttribute("fetchSize");
Integer timeout = context.getIntAttribute("timeout");
String parameterMap = context.getStringAttribute("parameterMap");
String parameterType = context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = resolveClass(parameterType);
String resultMap = context.getStringAttribute("resultMap");
String resultType = context.getStringAttribute("resultType");
String lang = context.getStringAttribute("lang");
LanguageDriver langDriver = getLanguageDriver(lang);
Class<?> resultTypeClass = resolveClass(resultType);
String resultSetType = context.getStringAttribute("resultSetType");
StatementType statementType = StatementType.valueOf(
context.getStringAttribute("statementType", StatementType.PREPARED));
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
// ========== 第四步:获取SQL文本 ==========
String statementId = builderAssistant.getCurrentNamespace() + "." + id;
// ========== 第五步:构建MappedStatement ==========
// ★★★★★ 核心:创建MappedStatement对象 ★★★★★
builderAssistant.addMappedStatement(
id,
sqlSource,
statementType,
sqlCommandType,
fetchSize,
timeout,
parameterMap,
parameterTypeClass,
resultMap,
resultTypeClass,
resultSetTypeEnum,
flushCache,
useCache,
resultOrdered,
keyGenerator,
keyProperty,
keyColumn,
databaseId,
langDriver,
resultSets);
}这个方法的核心就是创建MappedStatement对象。
MappedStatement是啥?
public final class MappedStatement {
// SQL ID(namespace + method name)
private String id;
// SQL类型(SELECT、INSERT、UPDATE、DELETE)
private SqlCommandType sqlCommandType;
// SQL源(动态SQL或静态SQL)
private SqlSource sqlSource;
// 参数类型
private Class<?> parameterType;
// 结果映射
private ResultMap resultMap;
// 返回类型
private Class<?> resultType;
// 语句类型(STATEMENT、PREPARED、CALLABLE)
private StatementType statementType;
// ... 其他配置
}MappedStatement就是一条SQL语句的完整配置信息!
七、bindMapperForNamespace:绑定Mapper接口
好,现在来看第2步——绑定Mapper接口。
核心方法6:bindMapperForNamespace
文件位置:mybatis-3/XMLMapperBuilder.java
private void bindMapperForNamespace() {
// 获取namespace
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
// 根据namespace加载类
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
// 如果没有找到类,忽略
}
if (boundType != null) {
// 检查是否已经绑定
if (!configuration.hasMapper(boundType)) {
// ★★★★★ 核心:将Mapper接口注册到Configuration ★★★★★
configuration.addMapper(boundType);
}
}
}
}这里的关键是configuration.addMapper(boundType),它会把Mapper接口注册到Configuration中。
核心方法7:addMapper
文件位置:mybatis-3/MapperRegistry.java
public <T> void addMapper(Class<T> type) {
// 必须是接口
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
// ★★★★★ 核心:创建MapperProxyFactory ★★★★★
MapperProxyFactory<T> factory = new MapperProxyFactory<>(type);
// 注册到knownMappers中
knownMappers.put(type, factory);
}
}看到了吧?这里创建了一个MapperProxyFactory,并注册到knownMappers中。
MapperProxyFactory是啥?
public class MapperProxyFactory<T> {
// Mapper接口类型
private final Class<T> mapperInterface;
// 方法缓存
private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
/**
* 创建Mapper代理对象
*/
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
// ★★★★★ 核心:使用JDK动态代理创建代理对象 ★★★★★
return (T) Proxy.newProxyInstance(
mapperInterface.getClassLoader(),
new Class[] { mapperInterface },
mapperProxy);
}
/**
* 创建Mapper代理对象
*/
public T newInstance(SqlSession sqlSession) {
// 创建MapperProxy
final MapperProxy<T> mapperProxy = new MapperProxy<>(
sqlSession, mapperInterface, methodCache);
// 创建代理对象
return newInstance(mapperProxy);
}
}看到了吗?MapperProxyFactory使用JDK动态代理创建Mapper接口的代理对象!
这就是为什么Mapper接口没有实现类,却能调用方法的原因——因为调用的是代理对象!
扩展场景4:Mapper接口是怎么被调用的?
// 当你调用:
User user = userMapper.selectById(1);
// 实际执行流程:
// 1. userMapper是代理对象(MapperProxy)
// 2. 调用selectById()方法,会进入MapperProxy.invoke()
// 3. MapperProxy.invoke()会找到对应的MappedStatement
// 4. 调用SqlSession.selectOne()执行SQL
// 5. 返回结果八、SqlSessionFactoryBuilder.build():最终构建
好,现在来看最后一步——构建SqlSessionFactory。
核心方法8:build
文件位置:mybatis-3/SqlSessionFactoryBuilder.java
package org.apache.ibatis.session;
import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;
import java.io.InputStream;
/**
* SqlSessionFactory构建器
* 负责构建SqlSessionFactory
*/
public class SqlSessionFactoryBuilder {
/**
* 构建SqlSessionFactory
*/
public SqlSessionFactory build(Configuration config) {
// ★★★★★ 核心:创建DefaultSqlSessionFactory ★★★★★
return new DefaultSqlSessionFactory(config);
}
}就这么简单!传入Configuration对象,返回DefaultSqlSessionFactory实例。
DefaultSqlSessionFactory是啥?
public class DefaultSqlSessionFactory implements SqlSessionFactory {
// 配置对象
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
@Override
public SqlSession openSession() {
// 打开SqlSession
return openSessionFromDataSource(
configuration.getDefaultExecutorType(), null, false);
}
@Override
public SqlSession openSession(boolean autoCommit) {
// 打开SqlSession(指定自动提交)
return openSessionFromDataSource(
configuration.getDefaultExecutorType(), null, autoCommit);
}
}DefaultSqlSessionFactory的作用就是创建SqlSession。
九、完整的SqlSessionFactory创建流程图
说了这么多,咱们画个图把整个流程串起来:
Spring Boot启动
↓
┌──────────────────────────────────────────────┐
│ MybatisAutoConfiguration │
│ │
│ @ConditionalOnClass检查 │
│ @ConditionalOnSingleCandidate检查 │
│ @AutoConfigureAfter检查 │
└─────────────┬─────────────────────────────────┘
↓
┌──────────────────────────────────────────────┐
│ SqlSessionFactoryBean │
│ │
│ 1. 设置DataSource │
│ 2. 设置configLocation │
│ 3. 设置mapperLocations │
│ 4. 设置typeHandlers │
│ 5. 设置plugins │
│ 6. 设置configurationProperties │
└─────────────┬─────────────────────────────────┘
↓
┌──────────────────────────────────────────────┐
│ afterPropertiesSet() │
│ → buildSqlSessionFactory() │
│ │
│ 1. 创建Configuration │
│ 2. 解析mybatis-config.xml │
│ 3. 注册类型别名 │
│ 4. 注册类型处理器 │
│ 5. 注册插件 │
│ 6. 解析Mapper XML文件 │
│ → XMLMapperBuilder.parse() │
│ → configurationElement() │
│ → 解析<cache> │
│ → 解析<resultMap> │
│ → 解析<select>/<insert>/... │
│ → 创建MappedStatement │
│ → bindMapperForNamespace() │
│ → configuration.addMapper() │
│ → 注册MapperProxyFactory │
│ 7. SqlSessionFactoryBuilder.build() │
│ → new DefaultSqlSessionFactory(config) │
└──────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────┐
│ SqlSessionFactory创建完成 │
│ │
│ 包含: │
│ - Configuration(所有配置) │
│ - MappedStatement(所有SQL) │
│ - MapperProxyFactory(所有Mapper) │
│ - TypeHandlerRegistry(类型处理器) │
│ - TypeAliasRegistry(类型别名) │
└──────────────────────────────────────────────┘十、关键问题解答
Q1:为什么要用FactoryBean,而不是直接创建?
答案:因为SqlSessionFactory的创建过程很复杂,需要解析配置文件、Mapper XML等,用FactoryBean可以封装这些复杂逻辑。
Q2:MappedStatement是什么时候创建的?
答案:在启动时,解析Mapper XML时创建。每个<select>、<insert>等节点都会创建一个MappedStatement。
Q3:Mapper接口没有实现类,为什么能调用?
答案:因为使用了JDK动态代理,创建了代理对象。调用方法时,会进入MapperProxy.invoke()。
Q4:Configuration对象里都存了什么?
答案:所有MyBatis的配置信息:
- MappedStatement(所有SQL语句)
- ResultMap(结果映射)
- TypeHandler(类型处理器)
- TypeAlias(类型别名)
- Plugin(插件)
- ...
十一、总结与伏笔
通过这篇文章的分析,咱们搞清楚了MyBatis的自动装配和SqlSessionFactory的创建流程:
- 自动装配:MybatisAutoConfiguration检查条件,创建SqlSessionFactoryBean
- FactoryBean机制:Spring容器调用getObject()获取SqlSessionFactory
- buildSqlSessionFactory():9步构建流程
- 解析Mapper XML:XMLMapperBuilder解析所有节点
- 创建MappedStatement:每个SQL语句对应一个MappedStatement
- 绑定Mapper接口:注册MapperProxyFactory
- 构建SqlSessionFactory:创建DefaultSqlSessionFactory
但是,咱们还有个关键问题没深入:
SqlSession是怎么工作的?
- openSession()是怎么创建SqlSession的?
- Executor执行器是怎么创建的?
- SimpleExecutor、ReuseExecutor、BatchExecutor有什么区别?
- MyBatis是怎么和Spring事务整合的?
这些问题,将在下一篇文章《SqlSession与Executor执行器的创建》中为你揭晓。到时候咱们会深入到SqlSession的源码,看看它是怎么管理数据库会话的。
敬请期待!
参考资料
- MyBatis 3.5.x 源码
src/main/java/org/apache/ibatis/session/SqlSessionFactory.javasrc/main/java/org/apache/ibatis/session/defaults/DefaultSqlSessionFactory.javasrc/main/java/org/apache/ibatis/builder/xml/XMLMapperBuilder.java
- MyBatis-Spring 2.1.x 源码
src/main/java/org/mybatis/spring/SqlSessionFactoryBean.java
- MyBatis-Spring-Boot-Autoconfigure 2.7.x 源码
src/main/java/org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.java
- 《MyBatis技术内幕》
- 《Spring源码深度解析》