全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-20 8 分钟 ✍️ juanwangdev

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 + mappedStatementsSQL语句映射

要点总结

  • 解析顺序严格:properties → settings → typeAliases → plugins → environments → typeHandlers → mappers,Mapper 必须最后解析
  • Configuration 是核心:所有配置最终都集中存储到 Configuration 对象,它是 MyBatis 的全局上下文
  • Mapper 注册分两步:先解析 XML 中的 SQL 语句构建 MappedStatement,再绑定 Mapper 接口建立方法映射
  • 防重复加载机制:通过 loadedResources Set 记录已加载资源,避免 Mapper 重复注册
  • 延迟解析 pending 语句:如果语句引用了尚未解析的 ResultMap,会暂存到 pending 集合,最后统一重试解析
  • SqlSessionFactory 本质:仅是 Configuration 对象的轻量级包装器,不包含实际数据库连接

📝 发现内容有误?点击此处直接编辑

← 上一篇 SqlSession 生命周期
下一篇 → StatementHandler 语句处理
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库