SqlSessionFactory 构建流程
MyBatis 的 SqlSessionFactory 构建过程是将 XML 配置转换为内存中 Configuration 对象的核心流程,理解此过程对掌握 MyBatis 运行机制至关重要。
构建入口
Java
// 典型构建方式
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
调用链:SqlSessionFactoryBuilder.build() -> XMLConfigBuilder.parse() -> build(Configuration)
XMLConfigBuilder 解析流程
配置文件结构
XML
<configuration>
<properties resource="db.properties"/>
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
<typeAliases>
<package name="com.example.model"/>
</typeAliases>
<plugins>
<plugin interceptor="com.example.MyPlugin"/>
</plugins>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
<package name="com.example.mapper"/>
</mappers>
</configuration>
解析顺序与核心方法
Java
public class XMLConfigBuilder extends BaseBuilder {
public Configuration parse() {
if (parsed) throw new BuilderException("Each XMLConfigBuilder can only be used once.");
parsed = true;
// 从根节点 <configuration> 开始解析
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
// 1. 解析 <properties> - 外部属性文件
propertiesElement(root.evalNode("properties"));
// 2. 解析 <settings> - 全局设置
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
configuration.setSettings(settings);
// 3. 解析 <typeAliases> - 类型别名
typeAliasesElement(root.evalNode("typeAliases"));
// 4. 解析 <plugins> - 拦截器
pluginElement(root.evalNode("plugins"));
// 5. 解析 <objectFactory> - 对象工厂
objectFactoryElement(root.evalNode("objectFactory"));
// 6. 解析 <objectWrapperFactory> - 对象包装工厂
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// 7. 解析 <reflectorFactory> - 反射工厂
reflectorFactoryElement(root.evalNode("reflectorFactory"));
// 8. 应用 settings 到 Configuration
settingsElement(settings);
// 9. 解析 <environments> - 数据源与事务管理器
environmentsElement(root.evalNode("environments"));
// 10. 解析 <databaseIdProvider> - 数据库厂商标识
databaseIdPropertyElement(root.evalNode("databaseIdProvider"));
// 11. 解析 <typeHandlers> - 类型处理器
typeHandlerElement(root.evalNode("typeHandlers"));
// 12. 解析 <mappers> - Mapper注册(最后一步)
mapperElement(root.evalNode("mappers"));
}
}
解析顺序关键点:Mapper 必须最后解析,因为 Mapper 中引用的类型别名、类型处理器等必须先注册完成。
各模块解析细节
1. Properties 解析
Java
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
// 先读取子节点定义的 property
Properties defaults = context.getChildrenAsProperties();
// 获取 resource/url 属性,加载外部配置文件
String resource = context.getStringAttribute("resource");
String url = context.getStringAttribute("url");
if (resource != null) {
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
defaults.putAll(Resources.getUrlAsProperties(url));
}
// 将合并后的 properties 设置到 configuration 和 parser
configuration.setVariables(defaults);
parser.setVariables(defaults);
}
}
支持两种属性来源:<property> 子节点 + 外部 resource/url 文件,后者覆盖前者。
2. Settings 解析
Java
private void settingsElement(Properties props) {
configuration.setAutoMappingBehavior(
AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
configuration.setAutoMappingUnknownColumnBehavior(
AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled", "true")));
configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled", "false")));
configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading", "false")));
configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled", "true")));
configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel", "true")));
configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys", "false")));
configuration.setDefaultExecutorType(
ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
// ... 更多设置
}
所有设置项最终都存储到 Configuration 对象的对应字段中。
3. TypeAliases 解析
Java
private void typeAliasesElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
// 扫描整个包,自动注册别名
String typeAliasPackage = child.getStringAttribute("name");
configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
} else {
// 单个类型注册
String type = child.getStringAttribute("type");
String alias = child.getStringAttribute("alias");
configuration.getTypeAliasRegistry().registerAlias(alias, type);
}
}
}
}
Java
注册流程:
package扫描 → ClassPathResource扫描 → @Alias注解识别 → 别名注册到Map<String, Class<?>>
4. Plugins 解析
Java
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
// 实例化拦截器
Interceptor instance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();
instance.setProperties(properties);
// 添加到 Configuration.interceptorChain
configuration.addInterceptor(instance);
}
}
}
插件按照配置顺序注册到 InterceptorChain,执行时形成责任链。
5. Environments 解析
XML
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
String id = context.getStringAttribute("default");
for (XNode child : context.getChildren()) {
String childId = child.getStringAttribute("id");
if (id.equals(childId)) {
// 解析事务管理器
if (child.evalNode("transactionManager") != null) {
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
// 解析数据源
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
// 构建 Environment 对象
Environment.Builder environmentBuilder = new Environment.Builder(childId)
.transactionFactory(txFactory)
.dataSource(dataSource);
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
}
注意:默认只加载
default指定的 environment,其他环境定义会被忽略。
Mapper XML 注册机制
三种注册方式
Java
<mappers>
<!-- 1. resource: classpath下的XML文件 -->
<mapper resource="mapper/UserMapper.xml"/>
<!-- 2. url: 绝对路径或网络路径 -->
<mapper url="file:///var/mappers/UserMapper.xml"/>
<!-- 3. class: Mapper接口注解方式 -->
<mapper class="com.example.mapper.UserMapper"/>
<!-- 4. package: 批量注册接口 -->
<package name="com.example.mapper"/>
</mappers>
Mapper XML 解析流程
Java
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
// 包扫描方式
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
// 单个Mapper
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null) {
// XML方式:先解析XML,再注册接口
configuration.addLoadedResource(resource);
parser.parse(); // 调用 XMLMapperBuilder.parse()
} else if (url != null) {
configuration.addLoadedResource(url);
parser.parse();
} else if (mapperClass != null) {
// 注解方式:直接注册接口
Class<?> mapperInterface = resolveClass(mapperClass);
configuration.addMapper(mapperInterface);
}
}
}
}
}
XMLMapperBuilder 解析过程
Java
public class XMLMapperBuilder extends BaseBuilder {
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
// 1. 解析 <mapper> 根节点
configurationElement(parser.evalNode("/mapper"));
// 2. 标记为已加载,防止重复
configuration.addLoadedResource(resource);
// 3. 绑定 Mapper 接口
bindMapperForNamespace();
}
// 4. 处理之前未解析成功的语句
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
private void configurationElement(XNode context) {
String namespace = context.getStringAttribute("namespace");
// 1. 设置 namespace
builderAssistant.setCurrentNamespace(namespace);
// 2. 解析 <cache-ref> / <cache>
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
// 3. 解析 <parameterMap>(已废弃)
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
// 4. 解析 <resultMap>
resultMapElements(context.evalNodes("/mapper/resultMap"));
// 5. 解析 <sql> 片段
sqlElement(context.evalNodes("/mapper/sql"));
// 6. 解析 <select|insert|update|delete> 语句
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
}
}
bindMapperForNamespace 绑定过程
Java
private void bindMapperForNamespace() {
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
// namespace 不是有效类,忽略
}
if (boundType != null && !configuration.hasMapper(boundType)) {
configuration.addLoadedResource("namespace:" + namespace);
configuration.addMapper(boundType); // 注册到 MapperRegistry
}
}
}
关键:
namespace必须与 Mapper 接口的全限定名一致,否则无法建立 XML 语句与接口方法的映射。
构建 SqlSessionFactory
text
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
解析完成后,Configuration 对象包含所有配置信息,直接包装为 DefaultSqlSessionFactory。
text
Configuration 核心字段:
├── environment // 数据源 + 事务管理器
├── mapperRegistry // 所有 Mapper 接口注册表
├── typeAliasRegistry // 类型别名
├── typeHandlerRegistry // 类型处理器
├── interceptorChain // 拦截器链
├── mappedStatements // MappedStatement 映射(key: namespace.statementId)
├── resultMaps // ResultMap 映射
├── caches // 二级缓存映射
└── settings // 全局设置
对象构建时序图
text
调用方 SqlSessionFactoryBuilder XMLConfigBuilder Configuration
| | | |
|--build(inputStream)--------->| | |
| |--new XMLConfigBuilder->| |
| |--parse()-------------->| |
| | |--parseConfiguration()|
| | |--propertiesElement()->|setVariables()
| | |--settingsElement()--->|setSettings()
| | |--typeAliasesElement()->|registerAlias()
| | |--pluginElement()------>|addInterceptor()
| | |--environmentsElement()->|setEnvironment()
| | |--mapperElement()----->|addMapper()
| | | |--buildStatement()
| | |<-------------------|
| |<--Configuration--------| |
| |--build(Configuration)->| |
|<--DefaultSqlSessionFactory-| | |
对比总结
| 配置项 | 解析时机 | 存储位置 | 作用 |
|---|---|---|---|
| properties | 最先解析 | Configuration.variables | 占位符替换 |
| settings | 早期 | Configuration各字段 | 全局行为控制 |
| typeAliases | 中期 | TypeAliasRegistry | 类型别名映射 |
| plugins | 中期 | InterceptorChain | 拦截器注册 |
| environments | 中后期 | Configuration.environment | 数据源配置 |
| mappers | 最后 | MapperRegistry + mappedStatements | SQL语句映射 |
要点总结
- 解析顺序严格:properties → settings → typeAliases → plugins → environments → typeHandlers → mappers,Mapper 必须最后解析
- Configuration 是核心:所有配置最终都集中存储到 Configuration 对象,它是 MyBatis 的全局上下文
- Mapper 注册分两步:先解析 XML 中的 SQL 语句构建 MappedStatement,再绑定 Mapper 接口建立方法映射
- 防重复加载机制:通过
loadedResourcesSet 记录已加载资源,避免 Mapper 重复注册 - 延迟解析 pending 语句:如果语句引用了尚未解析的 ResultMap,会暂存到 pending 集合,最后统一重试解析
- SqlSessionFactory 本质:仅是 Configuration 对象的轻量级包装器,不包含实际数据库连接
📝 发现内容有误?点击此处直接编辑