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 支持功能增强(如分页、监控)。
相关推荐
钢铁男儿3 分钟前
C# 类和继承(扩展方法)
java·servlet·c#
饮长安千年月10 分钟前
JavaSec-SpringBoot框架
java·spring boot·后端·计算机网络·安全·web安全·网络安全
移动开发者1号11 分钟前
Android 大文件分块上传实战:突破表单数据限制的完整方案
android·java·kotlin
代码匠心11 分钟前
从零开始学Flink:揭开实时计算的神秘面纱
java·大数据·后端·flink
jie1889457586626 分钟前
C++ 中的 const 知识点详解,c++和c语言区别
java·c语言·c++
Livingbody30 分钟前
Transformers Pipeline 加载whisper模型实现语音识别ASR
后端
网安INF32 分钟前
RSA加密算法:非对称密码学的基石
java·开发语言·密码学
蔡蓝37 分钟前
设计模式-观察着模式
java·开发语言·设计模式
异常君1 小时前
@Bean 在@Configuration 中和普通类中的本质区别
java·spring·面试
jackson凌1 小时前
【Java学习笔记】Math方法
java·笔记·学习