mybatis源码-深入分析加载配置的流程

知其然要知其所以然,探索每一个知识点背后的意义,你知道的越多,你不知道的越多,一起学习,一起进步,如果文章感觉对您有用的话,关注、收藏、点赞,有困惑的地方请评论,我们一起交流!


一、MyBatis 启动加载环境配置流程图

二、流程详解与源码分析

1. 入口:SqlSessionFactoryBuilder.build()

  • 作用 :读取配置文件流,初始化 XMLConfigBuilder

  • 源码路径org.apache.ibatis.session.SqlSessionFactoryBuilder

    java 复制代码
    public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      return build(parser.parse());
    }

2. 解析全局配置:XMLConfigBuilder.parse()

  • 源码路径org.apache.batis.builder.xml.XMLConfigBuilder

  • 关键方法parse() 逐级解析配置文件节点:

    java 复制代码
    public Configuration parse() {
      // 解析 <configuration> 根节点
      parseConfiguration(parser.evalNode("/configuration"));
      return configuration;
    }
    
    private void parseConfiguration(XNode root) {
      // 依次解析各配置块
      propertiesElement(root.evalNode("properties"));
      environmentsElement(root.evalNode("environments"));
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      mapperElement(root.evalNode("mappers"));
    }

3. 解析环境配置(environments)

  • 源码路径XMLConfigBuilder.environmentsElement()
  • 功能:加载数据源和事务管理器。
  • 核心对象
    • Environment:包含 DataSourceTransactionFactory
    • TransactionFactory:决定事务管理方式(JDBC/MANAGED)。

4. 解析类型别名(typeAliases)

  • 源码路径XMLConfigBuilder.typeAliasesElement()
  • 功能 :注册 Java 类型别名(如 com.example.UserUser)。
  • 核心对象TypeAliasRegistry,内部维护 Map<String, Class<?>>

5. 解析插件(plugins)

  • 源码路径XMLConfigBuilder.pluginElement()
  • 功能:加载拦截器(如分页插件),构建拦截器链。
  • 核心对象
    • InterceptorChain:通过动态代理包装目标对象(如 Executor)。
    • 示例:@Intercepts 注解定义拦截点。

6. 解析映射器(mappers)

  • 源码路径XMLConfigBuilder.mapperElement()

  • 功能:加载 Mapper 接口或 XML 文件,绑定 SQL 语句。

  • 关键分支逻辑

    java 复制代码
    // 处理 <mapper> 标签的不同配置方式
    if (resource != null) {
      // 从类路径加载 XML 文件
      InputStream inputStream = Resources.getResourceAsStream(resource);
      XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, ...);
      mapperParser.parse();
    } else if (url != null) {
      // 从 URL 加载 XML 文件
    } else if (mapperClass != null) {
      // 注册 Mapper 接口
      configuration.addMapper(mapperClass);
    }

7. 解析 Mapper XML:XMLMapperBuilder.parse()

  • 源码路径org.apache.ibatis.builder.xml.XMLMapperBuilder

  • 关键逻辑

    java 复制代码
    public void parse() {
      // 解析 <mapper> 根节点
      configurationElement(parser.evalNode("/mapper"));
    }
    
    private void configurationElement(XNode context) {
      // 解析命名空间
      String namespace = context.getStringAttribute("namespace");
      // 解析 <select>, <insert> 等 SQL 节点
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    }

8. 绑定 Mapper 接口

  • 源码路径org.apache.ibatis.binding.MapperRegistry

  • 核心方法addMapper()

    java 复制代码
    public <T> void addMapper(Class<T> type) {
      MapperProxyFactory<T> proxyFactory = new MapperProxyFactory<>(type);
      knownMappers.put(type, proxyFactory);
      // 解析接口方法对应的 SQL 语句
      MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
      parser.parse();
    }

9. 生成 MappedStatement

  • 源码路径org.apache.ibatis.builder.MapperBuilderAssistant

  • 核心逻辑 :将 XML 或注解中的 SQL 定义转换为 MappedStatement

    java 复制代码
    public MappedStatement addMappedStatement(
        String id, SqlSource sqlSource, ...) {
      // 构建 MappedStatement 并注册到 Configuration
      MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, ...);
      MappedStatement statement = statementBuilder.build();
      configuration.addMappedStatement(statement);
      return statement;
    }

10. 构建 SqlSessionFactory

  • 最终产物DefaultSqlSessionFactory

  • 源码路径SqlSessionFactoryBuilder.build()

    java 复制代码
    public SqlSessionFactory build(Configuration config) {
      return new DefaultSqlSessionFactory(config);
    }

三、关键类图

plaintext 复制代码
SqlSessionFactoryBuilder
    │
    └── build() → XMLConfigBuilder
                    │
                    └── parse() → Configuration
                                    │
                                    ├── Environment
                                    ├── MappedStatement
                                    ├── TypeAliasRegistry
                                    └── InterceptorChain
                                            │
                                            └── Plugin.wrap() → 动态代理对象

四、调试技巧

  1. 断点位置

    • XMLConfigBuilder.parseConfiguration():跟踪全局配置解析。
    • XMLMapperBuilder.parse():观察 Mapper XML 的加载过程。
    • MapperRegistry.addMapper():分析 Mapper 接口的动态代理生成。
  2. 日志输出 : 在 mybatis-config.xml 中开启日志,查看配置加载细节:

    xml 复制代码
    <configuration>
      <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
      </settings>
    </configuration>

五、总结

通过上述流程,MyBatis 完成了从配置文件到运行时对象的转换,核心在于:

  1. 全局配置中心Configuration 对象存储所有配置信息。
  2. 动态代理机制MapperProxy 实现接口方法与 SQL 的绑定。
  3. 插件扩展InterceptorChain 支持功能增强(如分页、监控)。
相关推荐
陈随易7 小时前
FFmpeg 9.0 发布,代号 Lei,音视频处理再升级
前端·后端·程序员
凌虚10 小时前
面向 MySQL 用户的 PostgreSQL 快速上手指南
数据库·后端·架构
都叫我大帅哥11 小时前
Java JJWT 完全指南:从入门到生产级实践
java
IT_陈寒11 小时前
Vite静态资源引用这个坑我踩得有点疼
前端·人工智能·后端
前端开发张小七12 小时前
Java 学习笔记 · 第一课:基础语法与核心概念
java·后端
海盗123412 小时前
微软技术周报 ——2026-08-03
后端·python·microsoft·c#·.netcore
沙湖遇雨12 小时前
6. Pipeline 的生命周期
后端
Loveyourself12 小时前
cc压缩机制之-toolResultBudget源码解读
面试
青青子衿悠悠我心12 小时前
TRAE Work让财务月底对账3小时变5分钟
后端
创安电气研究所12 小时前
用TypeScript给Modbus设备做一层适配器
后端