1.环境准备
1.1 mybatis的介绍以及学习框架源码的目标
MyBatis是当前最流行的java持久层框架之一,其通过XML配置的方式消除了绝大部分JDBC重复代码以及参数的设置,结果集的映射。虽然mybatis是最为流行的持久层框架之一,但是相比其他开源框架比如spring/netty的源码来说,其注释相对而言显得比较少。为了更好地学习和理解mybatis背后的设计思路,作为高级开发人员,有必要深入研究了解优秀框架的源码,以便更好的借鉴其思想。同时,框架作为设计模式的主要应用场景,通过研究优秀框架的源码,可以更好的领会设计模式的精髓。学习框架源码和学习框架本身不同,我们不仅要首先比较细致完整的熟悉框架提供的每个特性,还要理解框架本身的初始化过程等,除此之外,更重要的是,我们不能泛泛的快速浏览哪个功能是通过哪个类或者接口实现和封装的,对于核心特性和初始化过程的实现,我们应该达到下列目标:
1. 对于核心特性和内部功能,具体是如何实现的,采用什么数据结构,边研究边思考这么做是否合理,或者不合理,尤其是很多的特性和功能的调用频率是很低的,或者很多集合中包含的元素数量在99%以上的情况下都是1,这种情况下很多设计决定并不需要特别去选择或者适合于大数据量或者高并发的复杂实现。对于很多内部的数据结构和辅助方法,不仅需要知道其功能本身,还需要知道他们在上下文中发挥的作用。
2. 对于核心特性和内部功能,具体实现采用了哪些设计模式,使用这个设计模式的合理性;
3. 绝大部分框架都被设计为可扩展的,mybatis也不例外,它提供了很多的扩展点,比如最常用的插件,语言驱动器,执行器,对象工厂,对象包装器工厂等等都可以扩展,所以,我们应该知道如有必要的话,如何按照要求进行扩展以满足自己的需求;
1.2 本系列源码解析的方式
首先,和从使用层面相同,我们在理解实现细节的时候,也会涉及到学习A的时候,引用到B中的部分概念,B又引用了C的部分概念,C反过来又依赖于D接口和A的相关接口操作,D又有很多不同的具体实现。在使用层面,我们通常不需要深入去理解B的具体实现,只要知道B的概念和可以提供什么特性就可以了。在实现层面,我们必须去全部掌握这所有依赖的实现,直到最底层JDK原生操作或者某些我们熟悉的类库为止。这实际上涉及到横向流程和纵向切面的交叉引用,以及两个概念的接口和多种实现之间的交叉调用。所以,我们在整个系列中会先按照业务流程横向的方式进行整体分析,并附上必要的流程图,在整个大流程完成之后,在一个专门的章节安排对关键的类进行详细分析,它们的适用场景,这样就可以交叉引用,又不会在主流程中夹杂太多的干扰内容。
其次,因为我们免不了会对源码的主要部分做必要的注释,所以,对源码的讲解和注释会穿插进行,对于某些部分通过注释后已经完全能够知道其含义的实现,就不会在多此一举的重述。另外大家可以在我个人 github 上面有相应注解。
1.3 环境搭建
两种方式初始环境搭建,第一种:如果觉得麻烦大家可以直接去我的个人github上面直接下载,可以直接运行项目;第二种:从https://github.com/mybatis/mybatis-3/releases下载mybatis3源代码,导入eclipse工程,执行maven clean install,本系列所使用的mybatis版本是3.4.6-SNAPSHOT,编写时的最新版本,读者如果使用其他版本,可能代码上会有细微差别,但其基本不影响我们对主要核心代码的分析。为了更好地理解mybatis的核心实现,我们需要创建一个演示工程mybatis-internal-example,读者可从github上下载,并将依赖的mybatis坐标改成mybatis-3.4.6-SNAPSHOT。
1.4 还是从经典的HelloWord开始我们的阅读源码之旅
要理解mybatis的内部原理,最好的方式是从头开始。所以,我们不用依赖spring集成开始。我们知道每一个MyBatis的应用程序都以一个SqlSessionFactory对象的实例为核心。SqlSessionFactory对象的实例可以又通过SqlSessionFactoryBuilder对象来获得。SqlSessionFactoryBuilder对象可以从XML配置文件加载配置信息,然后创建SqlSessionFactory;我们先看一下,下面的例子感受一下不依赖于spring的Mybatis:
- 创建主程序:
package org.apache.ibatis.example;
import java.io.IOException;
import java.io.Reader;
import org.apache.ibatis.example.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
/**
* @author: ruanhang
* @data: 2018年9月19日
*/
public class MybatisHelloWorld {
public static void main(String[] args) {
String resource = "org/apache/ibatis/example/configuration.xml";
Reader reader;
try {
reader = Resources.getResourceAsReader(resource);
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sqlMapper.openSession();
try {
User user = (User) session.selectOne("org.apache.ibatis.example.mapper.UserDao.selectOne", 1);
System.out.println(user.getId() + "," + user.getName());
} finally {
session.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
复制代码
- configuration.xml 配置文件编写
"1.0" encoding="UTF-8"?>
"-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
"development">
"development">
type="JDBC"/>
type="POOLED">
"driver" value="com.mysql.jdbc.Driver"/>
"url" value="jdbc:mysql://10.10.203.172/test?useUnicode=true"/>
"username" value="test"/>
"password" value="Jht123456"/>
"org/apache/ibatis/example/userMapper.xml"/>
复制代码
- mapper接口创建:
package org.apache.ibatis.example.mapper;
import org.apache.ibatis.example.pojo.User;
/**
* @author: ruanhang
* @data: 2018年9月19日
*/
public interface UserDao {
User selectOne(int id);
}
复制代码
- model实体类创建:
package org.apache.ibatis.example.pojo;
import java.io.Serializable;
import java.util.Date;
/**
* @author: ruanhang
* @data: 2018年9月19日
*/
public class User implements Serializable{
private static final long serialVersionUID = 1L;
private Integer id;
private String name;
private String password;
private Integer status;
private Date ct;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public Date getCt() {
return ct;
}
public void setCt(Date ct) {
this.ct = ct;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", password=" + password + ", status=" + status + ", ct=" + ct
+ "]";
}
}
复制代码
- mapper.xml 配置文件创建:
"1.0" encoding="UTF-8" ?>
"-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
"org.apache.ibatis.example.mapper.UserDao">
复制代码
表结构在个人github上面。
运行main方法,Console里面将会出现:
1,weilong
复制代码
总结:从上述代码可以看出,SqlSessionFactoryBuilder是一个关键的入口类,其中承担了mybatis配置文件的加载,解析,内部构建等职责。下一章,我们将重点分析mybatis配置文件的加载,配置类的构建等一系列细节。