1.2 万字深度解析MyBatis源码(完结)
昨天有球友问 MyBatis 源码方面的学习资料,刚好之前整理过,这里我们就从一个简单案例入手,接着就是一步一步的剥开 MyBatis 的源码,配合大量的高清无码图,发车🚗。
MyBatis 使用案例
添加 MyBatis 和 MySQL 相关 pom 依赖。
org.MyBatis
MyBatis
3.5.2
mysql
mysql-connector-java
8.0.16
runtime
本地创建数据库,创建一张表。同时可以初始化几条数据,方便后面 debug。
CREATE TABLE `t_user` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`pwd` varchar(255) DEFAULT NULL,
`gender` int DEFAULT NULL,
`age` int DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
实体类(entity):
public class User {
private Integer id;
private String userName;
private Integer age;
private Integer gender;
//省略.....
}
创建 UserMapper.java 接口:
public interface UserMapper {
User selectById(Integer id);
}
创建 UserMapper.xml 配置:
创建 MyBatis-config.xml 配置文件。
使用案例:
public class MyBatisApplication {
public static final String URL = "jdbc:mysql://localhost:3306/mblog?useUnicode=true";
public static final String USER = "root";
public static final String PASSWORD = "123456";
public static void main(String[] args) {
String resource = "MyBatis-config.xml";
InputStream inputStream = null;
SqlSession sqlSession = null;
try {
//读取MyBatis-config.xml
inputStream = Resources.getResourceAsStream(resource);
//解析MyBatis-config.xml配置文件,创建sqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//创建sqlSession
sqlSession = sqlSessionFactory.openSession();
//创建userMapper对象(UserMapper并没有实现类)
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//调用userMapper对象的方法
User user = userMapper.selectById(1);
System.out.println(user);
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭资源
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
sqlSession.close();
}
}
}
运行输出:
User{id=1, userName='沉默王二', age=23, gender=1}
就这么简单的搞定了,下面我们来详细的分析这个案例背后的设计原理。
猜想 MyBatis 是如何设计的
从上面的案例中,我们可以大致可以猜测到 MyBatis 一共做了哪些步骤。
1.定位到 MyBatis-config.xml 并读取装载。获取输入流 InputStream。
2.解析输入流 InputStream,把 MyBatis-config.xml 配置文件中相关配置项解析,校验,保存起来。
3.创建 sqlSessionFactory 对象,在我们的印象里,session 就是一次会话,所以我们可以理解 sqlSessionFactory 就是个工厂类,就专门创建 sqlSession 对象,并且这个 sqlSessionFactory 工厂类是唯一不变的(单例)。
4.创建 sqlSession,SqlSession 中肯定保存了配置文件内容信息和执行数据库相关的操作。
5.获取 userMapper 对象,但是 UserMapper 是接口,并且没有实现类。怎么就可以调用其方法呢?这里猜想可能用到了动态代理。
6.userMapper 接口中的方法是如何关联到 SQL 的,这个猜想可能是有个专门映射的类,另外,肯定使用到了接口全路径名+方法名称,这个才能确保方法和 SQL 关联(主要是使用的时候,都是方法名必须和 SQL 中 statementId 一致,由此猜想的)。
7.最后底层使用 JDBC 去操作数据库。
8.作为一个持久化框架,很有可能会使用到缓存,用来存储每次查询数据。
面试中遇到,让你来设计一个 MyBatis 如何设计?
配置文件解析
因为第一步是读取 MyBatis-config.xml 配置文件,这里我们就没必要阅读这部分源码了,这里得到是 InputStream 输入流。接下来就是基于这个输入流进行一系列牛逼的操作。
我们将从下面这行代码开始:
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSessionFactory 没有构造方法,那么这里使用的就是默认无参构造方法,所以我们直接进去 build 方法。
//这个方法啥也没干
public SqlSessionFactory build(InputStream inputStream) {
//调用的是另外一个build方法
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
//创建一个XMLConfigBuilder对象
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
该类中的 build 重载的方法如下:

SqlSessionFactory 中提供了三种读取配置信息的方法后:字节流、字符流和 Configuration 配置类。
创建 XMLConfigBuilder 对象,这个类是 BaseBuilder 的子类,BaseBuilder 类图:

看到这些子类基本上都是以 Builder 结尾,所以这里使用的是建造者设计模式。
这个类名可以猜出给类就是解析 xml 配置文件的。然后我们继续进入
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream,...);
}
MyBatis 对应解析包 org.apache.ibatis.parsing:

XPathParser 基于 Java XPath 解析器,用于解析 MyBatis 中
- MyBatis-config.xml
- mapper.xml
等 XML 配置文件 。
XPathParser 主要内容:

继续上面的源码分析:
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
构造一个 XMLConfigBuilder 对象,给属性设置相应值。
然后我们再回到 SqlSessionFactoryBuilder 中的 build 方法里:
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
build(parser.parse());
先看 parser.parse()方法:
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
//MyBatis-config.xml的一级标签
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
继续 parseConfiguration()方法:
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
...未能定位到原文的划线
企业级Agent工作流编排项目PaiFlow
Vibe Coding版本的PaiAgent
派聪明RAG AI知识库Java版本+Go版本
微服务 PmHub、技术派、MYDB
求职派JobClaw(OpenClaw/Hermes架构
PaiCLI(类似Claude Code的Agent
派简历(代码已完成)
等实战项目。
1. 微信扫右侧的优惠券加入知识星球
2. 解锁星球的实战项目教程和源码: 项目源码+教程获取
真诚点赞 诚不我欺
回复