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 源码的核心模块包括以下几个部分:
- 配置模块(Configuration)
-
作用:解析 XML 或注解配置,构建全局配置对象 Configuration。
-
关键类:
* SqlSessionFactoryBuilder:构建 SqlSessionFactory。
* XMLConfigBuilder:解析 mybatis-config.xml 文件。
* XMLMapperBuilder:解析 Mapper XML 文件。 -
流程:
1.加载 mybatis-config.xml,解析全局配置(数据源、插件、类型处理器等)。
2.解析 Mapper XML 文件或注解,生成 MappedStatement 对象(封装 SQL 信息)。
- SQL 执行模块(Executor)
-
作用:执行 SQL 并处理结果。
-
关键类:
Executor:执行器的接口,定义了 update、query 等方法。
SimpleExecutor:默认的简单执行器。
CachingExecutor:带二级缓存的执行器(装饰器模式)。 -
流程:
通过 Executor 执行 SQL。
结合 StatementHandler 创建 JDBC Statement。
通过 ResultSetHandler 处理结果集。
- 映射模块(Mapper)
-
作用:将 Java 接口与 SQL 映射关联。
-
关键类:
MapperProxy:动态代理对象,拦截 Mapper 接口方法调用。
MapperMethod:封装了 SQL 命令类型(SELECT/INSERT 等)和参数处理逻辑。 -
流程:
1.通过 MapperRegistry 注册 Mapper 接口。
2.使用 JDK 动态代理生成 MapperProxy 对象,将方法调用转发给 SqlSession。
二、SQL 执行流程源码解析
以下是 MyBatis 执行一条 SQL 的核心流程(以查询为例):
- 入口:Mapper 接口方法调用;
User user = userMapper.selectById(1);
- userMapper 是动态代理对象(MapperProxy),调用方法时触发 invoke()。
- 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);
}
}
- 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)
}
}
}
- 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);
}
}
- 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);
}
}
- 结果集处理
- ResultSetHandler 将 JDBC ResultSet 转换为 Java 对象:
public class DefaultResultSetHandler implements ResultSetHandler {
@Override
public List<Object> handleResultSets(Statement stmt) {
// 遍历 ResultSet,根据 ResultMap 映射字段到对象属性
}
}
三、关键设计解析
- 动态 SQL 解析
- 原理:基于 OGNL 表达式和 XMLScriptBuilder 解析动态标签(如 、)。
- 核心类:
- SqlSource:表示原始 SQL 内容(静态或动态)。
- DynamicSqlSource:处理动态 SQL,生成 BoundSQL(包含最终 SQL 和参数映射)。
- 插件机制(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;
}
}
- 缓存设计
缓存的设计师为了提高 数据的复用性,以及避免过度访问数据库造成点的消耗,由此提高了数据的查询速度。一级缓存在SqlSession级别提供快速的数据访问,而二级缓存则在整个应用程序范围内实现了数据的共享和重用。
- 一级缓存:
- 作用域:SqlSession 级别,默认开启。
- 实现:BaseExecutor 中的 localCache(PerpetualCache)。
- MyBatis在创建SqlSession对象时,会为其创建一个Executor对象来执行数据库操作。Executor对象内部维护了一个Cache缓存,用于存储查询结果。当执行查询操作时,Executor会先检查一级缓存中是否已存在该查询的结果,如果存在则直接返回;如果不存在,则执行查询操作,并将查询结果存入一级缓存中,以便后续使用
- 二级缓存:
- 作用域:Mapper 级别,需手动配置 。
- 实现:CachingExecutor 装饰器,通过 TransactionalCacheManager 管理缓存。
- 事务管理
- 原理:通过 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 的设计哲学,并为定制化开发(如编写插件、优化性能)提供理论支持。

8726

被折叠的 条评论
为什么被折叠?



