最近空闲下来决定读一读mybatis源码,先从启动入手,mybatis是一个轻量级ORM框架,它封装了对数据库的常用操作,我们用起来非常简单,可是对它的内部原理我们一无所知,这不利于我们调试和优化代码,因此最好的方式就是深入源码去理解其原理,下面,本文从mybatis启动来深入源码探究。
首先,举一个简单的例子来入手:
GetSqlSession.java:
package cn.just.configuration;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Properties;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class GetSqlSession {
private static SqlSessionFactory sqlSessionFactory;
private static Reader reader;
private static SqlSession sqlSession;
static{
try{
reader= Resources.getResourceAsReader("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); //
}catch(Exception e){
e.printStackTrace();
}
}
public static SqlSession GetSqlSession(){
try {
sqlSession=sqlSessionFactory.openSession();
System.out.println("创建SqlSession");
} catch (Exception e) {
e.printStackTrace();
}
return sqlSession;
}
}
BuilderConfig.java
package cn.just.configuration;
import org.apache.ibatis.session.SqlSession;
import cn.just.interfaceMapper.UserMapper;
import cn.just.mapper.Test;
public class BuilderConfig {
public static void main(String[] args) {
SqlSession sqlSession=GetSqlSession.GetSqlSession();
UserMapper user=sqlSession.getMapper(UserMapper.class);
Test test=user.selectById(1);
System.out.println(test.toString());
}
}
UserMapper .java
package cn.just.interfaceMapper;
import cn.just.mapper.Test;
public interface UserMapper {
public Test selectById(Integer id);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.just.interfaceMapper.UserMapper">
<select id="selectById" resultType="cn.just.mapper.Test">
select * from test where id=#{id}
</select>
</mapper>
上面是该实例的部分代码,全局配置以及数据库连接属性这里省略,非常简单,就是通过mybatis接口编程来从数据库查询对象。我们不加细讲上面代码,直接打断点开始阅读源码。
一、SqlSessionFactory启动原理
为了更方便理解源码,这里我先给出SqlSession创建过程导航图,然后我们来根据该图一步一步来进入源码探究。这样也不至于绕晕在源码中。
OK,从上面导航图我们可以看出,当项目启动首先通过SqlSessionFactoryBuilder的build(inputStream)方法进入创建SqlSession。因此我们打断点进入build方法:
进入build方法后我们会看到一个XmlConfigBuilder对象,它将参数传入并且创建,这里我们猜想,如果要想创建SqlSession就是要和数据库建立连接,因此它就要解析全局XML配置文件来读取数据源并且连接数据库,因此XmlConfigBuilder这个对象就是一个xml解析器。所以我们继续进入parse()方法。
进入parse方法后我们惊奇的发现它会创建一个Configuration对象,而这个Configuration对象封装了我们全局配置中的所有属性,比如settings,properties,mappers等等,这就很明显了,Configuration对象就是在parse解析全局XML配置文件后的一个封装类,它会通过这个类去创建建立数据库连接以及SqlSession的创建,我们接着往下走。
在这里,有一个要点,就是创建好Configuration后,它有一个MapperRegistry它封装着一个代理类,因为我们是通过接口变成的方式去操作数据库,因此它会为该接口创建一个代理类来实例化该接口。
最后,在疯转好所有的配置后,返回build方法创建一个DefaultSqlSessionFactory对象来创建回话。
到这里,我们就可以很清楚的看出mybatis创建SqlSessionFactory创建会话的原理了,最后,我们来探究一下它是如何通过接口来访问获取sql语句查询数据库的。
二、通过接口获取sql查询数据库
这里,我们就需要回到上面Configuration封装的那个类中去,我们可以在上图看出有一个很重要的对象MapperStatement对象,这个对象封装着对mapper文件的配置。
接着,我们进入mapperParser的parse方法中去。
很明显,它通过反射获取到UserMapper这个接口。我们接着跟下去。
从上面截图可以看出,它会创建一个MapperAnnotationBuilder对象,它封装着对数据库的各种操作,update,insert等等。
最后,我们进入parse防范,它通过反射来获取selectById方法,在parse方法执行后,它会调用 parsePendingMethods(),因此我们进入该方法,会看到它会通过封装好的Configuration对象来得到集合并且遍历,而在这个Conifguration对象中通过namespace获得了sql语句,最后操作数据库。
至此,我们就通过源码了解到SqlSessionFactory的启动原理以及通过接口产生代理对象来操作数据库的原理,以上如有什么不足或错误请多多指点。