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 支持功能增强(如分页、监控)。
相关推荐
新时代苦力工6 分钟前
处理对象集合,输出Map<String, Map<String, List<MyObject>>>格式数据,无序组合键处理方法
java·数据结构·list
极客智谷8 分钟前
Spring AI应用系列——基于Alibaba DashScope的聊天记忆功能实现
人工智能·后端
极客智谷10 分钟前
Spring AI应用系列——基于Alibaba DashScope实现功能调用的聊天应用
人工智能·后端
RJiazhen10 分钟前
5分钟让你的服务接入AI——用 API Auto MCP Server 实现大模型与后端系统的无缝对话
后端·开源·mcp
六边形66611 分钟前
Vue中的 ref、toRef 和 toRefs 有什么区别
前端·vue.js·面试
前端付豪12 分钟前
2、ArkTS 是什么?鸿蒙最强开发语言语法全讲解(附实操案例)
前端·后端·harmonyos
前端付豪16 分钟前
8、鸿蒙动画开发实战:做一个会跳舞的按钮!(附动效示意图)
前端·后端·harmonyos
前端付豪16 分钟前
3、构建你的第一个鸿蒙组件化 UI 页面:实现可复用的卡片组件(附实战代码)
前端·后端·harmonyos
Java水解16 分钟前
Feign结构与请求链路详解及面试重点解析
后端
前端付豪19 分钟前
7、打造鸿蒙原生日历组件:自定义 UI + 数据交互(附实操案例与效果图)
前端·后端·harmonyos