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:包含 DataSource 和 TransactionFactory。
    • TransactionFactory:决定事务管理方式(JDBC/MANAGED)。

4. 解析类型别名(typeAliases)

  • 源码路径 :XMLConfigBuilder.typeAliasesElement()
  • 功能 :注册 Java 类型别名(如 com.example.User → User)。
  • 核心对象 :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 支持功能增强(如分页、监控)。
相关推荐
考研保研资料分享12 小时前
四非会计保研中国矿大(北京):七场考核、候补与择校复盘
面试
划破黑暗的第一缕曙光12 小时前
[C++]:5.STL:string(2)
java·c++·redis
hhhjhl13 小时前
Undici 跨端三方库仓颉适配全流程
java·开发语言·ubuntu
wangchunyu11413 小时前
JAVA JVM内存结构与运行时数据区
java·开发语言·jvm
光锥智能13 小时前
海康机器人举行2026移动机器人新品发布会:箱式存拣升级,「明智」系列发布
java·前端·机器人
行思理13 小时前
Nginx 跨站请求(CORS)配置完全指南:从原理到生产实践
java·前端·nginx
小溪学编程13 小时前
Spring @Bean 与 @Component 注解的区别详解
java·后端·spring
IT_陈寒14 小时前
Python的列表推导式用不好真的会爆内存你知道吗
前端·人工智能·后端
Wang's Blog14 小时前
Java 项目实战: 交友项目 - 圈子互动章总结与自检清单
java·bootstrap·交友
xyouyuweizhu14 小时前
字节流与字符流:Java IO 的两条主线该怎么选
java·开发语言·python