两天前,舍友大方去面试了一家小公司
面试官:看你的简历上写着 ”熟练使用MyBatis“,你对 MyBatis 很了解咯
大方:是的,我能熟练使用 MyBatis 的增删查改,以及动态查询
面试官:那你看过 MyBatis 的底层源码吗?
大方:啊,这个我没有看过诶…
面试官:没事,你回去等通知吧
那如何简洁地回答 MyBatis 的源码呢?
相信大家对 MyBatis 的构建流程已经很熟悉了吧!
public static void init() throws IOException {
// 加载MyBatis.xml文件
in = Resources.getResourceAsStream("classpath:MyBatis-Config.xml");
// 通过SqlSessionFactoryBuilder的build方法加载输入流,构建SqlSessionFactory
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
// 执行OpenSession方法
session = factory.openSession();
// 调用getMapper获取mapper接口类
videoMapper = session.getMapper(VideoMapper.class);
}
顺着这个流程,我们简单讲一下底层原理
getResourceAsStream() 是一个加载文件并转化为输入流的方法,这个就不需要多说了。
重点放在这个build方法,我们看看build方法中写了什么
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// 通过 Xml解析器解析mybatis.xml文件,并调用parse方法
return build(parser.parse());
} catch (Exception e) {
} finally {
try {
inputStream.close();
} catch (IOException e) {
}
}
}
这个parse就是核心解析方法了,他xml文件中每个节点进行解析,解析并将数据加载到框架中
看他加载了这么多节点,一开始他解析最大的父节点《configuration》,解析出多种子节点并对子节点接着解析。
我们重点看这两个节点的解析,一个是environment节点的解析,解析完成后加载mybatis环境
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
if (isSpecifiedEnvironment(id)) {
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
// 获取数据源,我们已经编写好的url、driver、username、password
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
// 通过构建器模式构建出环境,并设置到configuration中
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
上面这一步设置环境是重点中的重点,有了上面这一步,mybatis才能正常启动起来
另外一个节点就是mapper节点,官网上写着mapper的几种实现方式:package、resource、url、class,这里就不详细介绍,这里是通过节点属性解析之后,获取mapper.xml的加载方式,最终解析并
最终,我们会进入这个方法
这个方法就是加载mapper.xml文件中sql语句的方法,加载完sql语句后,存放在mappedStatement中
至此,build构建方法加载完毕,接下来就是OpenSession方法
其实这个方法也很简单,重点就是这个 newTransaction 方法,他封装了 JDBC 底层的代码
诺!这些方法是不是很熟悉
有了 jdbc 的方法后,接下来就是 sql 语句的执行了
继续点击底层查看代码,在点击 MapperProxyFactory 这个类的时候,我们发现,mybatis 通过代理模式,动态地创建mapper
我们点击 MapperProxy 进入查看
在这个方法中,我们突然发现 MapperMethod 最后调用了 execute 解析方法!我们点进去查看一下这个解析方法实现了什么
哇哦!增删查改都写在这里了,四种类型的数据库操作方法,也同时对应封装好了返回结果
mybatis 的原理流程大概就是这样子过了一遍,当然,这不是全部,有很多我们没有深入的地方如XNode节点、缓存等等,有兴趣可以一起讨论。
哎呀!好像讲到这里有点乱,我来总结一下整个流程吧!
一开始,我们需要跟面试官说明一下,什么是 ORM 框架,实际上很多同学知道 ORM 框架有 MyBatis、Hibernate、Jpa等,但是并不能说明白 ORM 框架是什么?
ORM 框架就是将 Java 中的对象实体,与数据库表关联起来并一一对应的框架。比如,将 user 表和 User pojo 绑定在一起,将数据库中的 varchar 与 Java 中的 String 对应起来,
- 首先,调用 Resources.getResourceAsStream() 方法加载 MyBatis-Config.xml 文件
- 接着,通过 build() 方法加载输入流
- 通过 XmlConfigBuilder 执行 parse() 对xml 文件中的节点进行解析
- 在 parse 解析中,最重要解析的两个节点,分别是 environments 和 mappers
- 解析 environments ,并将其放到 configuration 中
- 解析 mappers,并将它们放到 kownmapper 中
- 接着执行 OpenSession 方法,这个方法封装了 jdbc 底层的数据库操作方法
- 通过 getMapper 方法,调用我们需要的 mapper 接口
- 并通过 jdk 的动态代理,为我们代理了 MapperMethod 类
- 调用 excute() 方法,我们发现这里已经写好了增删查改的方法,最终返回mybatis为我们封装好的result结果集
本文由mdnice多平台发布