MyBatis核心源码知识点

0 前言

MyBatis是什么?
解决了使用jdbc操作数据库的弊端,用JDBC去操作数据库,弊端有如下几点。
1.首先,数据库配置、SQL语句在代码中硬编码、可维护性差。
2.JDBC频繁创建和关闭连接数据库,消耗资源。利用连接池,避免反复连接和销毁。
3.一二级缓存, 查询语句不变的话,直接取缓存中的数据。
4.参数的设置,封装查询数据比较麻烦。
5.返回结果数据获取,需要封装,没有映射关系。
其中mybatis的底层本质上就是封装了JDBC,其结构图如下。
在这里插入图片描述

一、MyBatis源码解析

前置知识
为了加深对源码的理解,本文单独开启一个章节来描述JDK动态代理,动态代理,就是Java提供的一种在运行时创建代理对象的机制。
核心概念

  • Proxy类:这是JDK动态代理的核心类,位于java.lang.reflect包中。它提供了用于创建动态代理对象的方法,其中最关键的是newProxyInstance静态方法。
  • InvocationHandler接口:所有通过JDK动态代理创建的代理对象都需要实现这个接口。该接口定义了一个invoke方法,当代理对象上的方法被调用时,会触发这个方法。

工作原理

  • 通过Proxy.newProxyInstance方法创建一个实现了指定接口的代理对象。当调用代理对象的任何方法时,实际上会调用InvocationHandler的invoke方法。在invoke方法内部,可以根据需要自定义方法调用前的逻辑(如权限校验、日志记录等)、调用实际的目标方法,以及方法调用后的逻辑(如异常处理、资源释放等)。

MyBatis的执行过程可分为,解析和 执行两个步骤,就是对xml配置文件的读取并解析,并解析对应 的SQL语句。解析的标签有、别名、数据库连接、mapper扫描等信息。
MyBatis 源码的核心模块包括以下几个部分:

  1. 配置模块(Configuration)
  • 作用:解析 XML 或注解配置,构建全局配置对象 Configuration。

  • 关键类:
    * SqlSessionFactoryBuilder:构建 SqlSessionFactory。
    * XMLConfigBuilder:解析 mybatis-config.xml 文件。
    * XMLMapperBuilder:解析 Mapper XML 文件。

  • 流程:
    1.加载 mybatis-config.xml,解析全局配置(数据源、插件、类型处理器等)。
    2.解析 Mapper XML 文件或注解,生成 MappedStatement 对象(封装 SQL 信息)。

  1. SQL 执行模块(Executor)
  • 作用:执行 SQL 并处理结果。

  • 关键类:
    Executor:执行器的接口,定义了 update、query 等方法。
    SimpleExecutor:默认的简单执行器。
    CachingExecutor:带二级缓存的执行器(装饰器模式)。

  • 流程:
    通过 Executor 执行 SQL。
    结合 StatementHandler 创建 JDBC Statement。
    通过 ResultSetHandler 处理结果集。

  1. 映射模块(Mapper)
  • 作用:将 Java 接口与 SQL 映射关联。

  • 关键类:
    MapperProxy:动态代理对象,拦截 Mapper 接口方法调用。
    MapperMethod:封装了 SQL 命令类型(SELECT/INSERT 等)和参数处理逻辑。

  • 流程:
    1.通过 MapperRegistry 注册 Mapper 接口。
    2.使用 JDK 动态代理生成 MapperProxy 对象,将方法调用转发给 SqlSession。

二、SQL 执行流程源码解析

以下是 MyBatis 执行一条 SQL 的核心流程(以查询为例):

  1. 入口:Mapper 接口方法调用;
User user = userMapper.selectById(1);
  • userMapper 是动态代理对象(MapperProxy),调用方法时触发 invoke()。
  1. MapperProxy 动态代理
public class MapperProxy<T> implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
        // 将方法转换为 MapperMethod
        MapperMethod mapperMethod = cachedMapperMethod(method);
        return mapperMethod.execute(sqlSession, args);
    }
}
  1. MapperMethod 执行逻辑
public class MapperMethod {
    public Object execute(SqlSession sqlSession, Object[] args) {
        switch (command.getType()) {
            case SELECT:
                Object param = method.convertArgsToSqlCommandParam(args);
                return sqlSession.selectOne(command.getName(), param);
            // 处理其他命令类型(INSERT/UPDATE/DELETE)
        }
    }
}
  1. SqlSession 委托 Executor
public class DefaultSqlSession implements SqlSession {
    @Override
    public <T> T selectOne(String statement, Object parameter) {
        List<T> list = executor.query(statement, parameter, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
        return list.get(0);
    }
}
  1. Executor 执行查询
public class SimpleExecutor extends BaseExecutor {
    @Override
    public <E> List<E> query(...) {
        // 1. 获取 BoundSQL(解析动态 SQL 后的最终 SQL)
        BoundSQL boundSql = configuration.getMappedStatement(statement).getBoundSql(parameter);
        // 2. 创建 StatementHandler(如 PreparedStatementHandler)
        StatementHandler handler = configuration.newStatementHandler(...);
        // 3. 准备 Statement,设置参数
        Statement stmt = prepareStatement(handler, ...);
        // 4. 执行查询并处理结果
        return handler.query(stmt, resultHandler);
    }
}
  1. 结果集处理
    • ResultSetHandler 将 JDBC ResultSet 转换为 Java 对象:
public class DefaultResultSetHandler implements ResultSetHandler {
    @Override
    public List<Object> handleResultSets(Statement stmt) {
        // 遍历 ResultSet,根据 ResultMap 映射字段到对象属性
    }
}

三、关键设计解析

  1. 动态 SQL 解析
  • 原理:基于 OGNL 表达式和 XMLScriptBuilder 解析动态标签(如 、)。
  • 核心类:
  • SqlSource:表示原始 SQL 内容(静态或动态)。
  • DynamicSqlSource:处理动态 SQL,生成 BoundSQL(包含最终 SQL 和参数映射)。
  1. 插件机制(Interceptor)
  • 原理:基于动态代理和责任链模式,拦截四大组件:
    Executor、StatementHandler、ParameterHandler、ResultSetHandler。
  • 实现:
@Intercepts({@Signature(type=Executor.class, method="query", args={...})})
public class MyPlugin implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 前置逻辑
        Object result = invocation.proceed();
        // 后置逻辑
        return result;
    }
}
  1. 缓存设计
    缓存的设计师为了提高 数据的复用性,以及避免过度访问数据库造成点的消耗,由此提高了数据的查询速度。一级缓存在SqlSession级别提供快速的数据访问,而二级缓存则在整个应用程序范围内实现了数据的共享和重用。
  • 一级缓存:
    • 作用域:SqlSession 级别,默认开启。
    • 实现:BaseExecutor 中的 localCache(PerpetualCache)。
    • MyBatis在创建SqlSession对象时,会为其创建一个Executor对象来执行数据库操作。Executor对象内部维护了一个Cache缓存,用于存储查询结果。当执行查询操作时,Executor会先检查一级缓存中是否已存在该查询的结果,如果存在则直接返回;如果不存在,则执行查询操作,并将查询结果存入一级缓存中,以便后续使用
  • 二级缓存:
    • 作用域:Mapper 级别,需手动配置 。
    • 实现:CachingExecutor 装饰器,通过 TransactionalCacheManager 管理缓存。
  1. 事务管理
  • 原理:通过 Transaction 接口管理 JDBC 连接的事务。
    • JdbcTransaction:基于 JDBC 连接的事务。
    • ManagedTransaction:由容器(如 Spring)管理的事务。

四、源码调试建议

1.入口示例:

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = userMapper.selectById(1);

2.关键断点:

  • XMLConfigBuilder.parse():解析全局配置。
  • MapperProxy.invoke():Mapper 方法代理入口。
  • SimpleExecutor.doQuery():SQL 执行核心逻辑。
  • DefaultResultSetHandler.handleResultSets():结果集映射。

五、总结

MyBatis 源码通过模块化设计实现高效灵活的 ORM 功能:
1.配置解析:构建全局 Configuration 对象,统一管理所有组件。
2.动态代理:通过 MapperProxy 将接口调用映射到 SQL 执行。
3.执行链:Executor 协调 StatementHandler、ResultSetHandler 完成 SQL 执行和结果映射。
4.扩展性:插件机制和缓存设计提供了高度的可定制性。
通过深入源码,可以更好地理解 MyBatis 的设计哲学,并为定制化开发(如编写插件、优化性能)提供理论支持。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值