接上一篇文章
https://www.jianshu.com/p/0f0408e3c02f
把测试类贴过来,方便阅读
import com.xingchen.mybatis.entity.Country;
import com.xingchen.mybatis.mapper.CountryMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.Reader;
import java.util.List;
/**
* @author chen
* @Date 2018/10/24
* mybatis 源码阅读
* */
public class MyTest {
private static SqlSessionFactory sqlSessionFactory=null;
static {
try{
Reader reader= Resources.getResourceAsReader("mybatis/mybatis-config.xml");
sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);
reader.close();
}catch (Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) {
SqlSession sqlSession=sqlSessionFactory.openSession();
try {
CountryMapper countryMapper=sqlSession.getMapper(CountryMapper.class);
List<Country> countries=countryMapper.selectAll();
for(Country country:countries){
System.out.println(country);
}
}catch (Exception e){
e.printStackTrace();
}finally {
sqlSession.close();
}
}
}
首先是SqlSessionFactory的创建
SqlSessionFactory
是MyBatis
的核心类之一,其最重要对的功能就是提供创建MyBatis
的核心接口SqlSession
,所以需要首先创建SqlSessionFactory
,为此我们需要提供配置文件和相关参数。而MyBatis
是一个复杂的系统,采用建造者模式去创建SqlSessionFactory
,可以通过SqlSessionFactoryBuilder
去创建。主要分为两步:
-
第一步,通过
XMLConfigBuilder
解析XML配置文件,读取配置参数,并将读取的数据存入Configuration
类中。MyBatis
几乎所有的配置都是存在这里。 -
第二步,使用
Configuration
对象去构建SqlSessionFactory
。MyBatis
中SqlSessionFactory
是一个接口,而不是一个实现类,不能实例化。因此MyBatis
提供了一个默认的SqlSessionFactory
实现类DefaultSqlSessionFactory
。大部分情况下,我们都没有必要去创建新的SqlSessionFactory
。
SqlSessionFactory
是通过SqlSessionFactoryBuilder
的build()
方法来创建的,从名字中很明显看的出这里用到了建造者模式。
下面就看看build()
是如何创建SqlSessionFactory
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()方法先创建了一个XMLConfigBuilder,从名字不难看出来这是个解析XML配置文件的类。我们来看看parser
的创建过程。
public class XMLConfigBuilder extends BaseBuilder {
...
public XMLConfigBuilder(Reader reader, String environment, Properties props) {
// 这个this是真正执行创建XMLConfigBuilder的方法
this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
}
...
}
对的就是下面这个方法。先初始化初始化Configuration
对象,然后设置一些属性,最终创建了XMLConfigBuilder
对象。
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
// 执行父类的构造方法,初始化Configuration 对象
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
在
SqlSessionFactory
构建中,Configuration
是重要的,MyBatis
的配置信息都会来源于次。作用如下:
- 读入配置文件,包括基础配置的XML文件和映射器的XML文件。
- 初始化基础配置,比如
MyBatis
的别名等,一些重要的类对象,例如,插件、映射器、ObjectFactory和typeHandle
对象。- 提供单例,为后续创建
SessionFactory
服务并提供配置的参数。- 执行一些重要的对象方法,初始化配置信息。
初始化Configuration
对象的时候,将一些别名注册到typeAliasRegistry
容器中。如下:
// Configuration类 无参构造方法
public Configuration() {
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
···
}
获取到了 XMLConfigBuilder
对象之后,则进入了下一步的处理parser.parse()
解析配置文件。
// XMLConfigBuilder 类
// 我们来看看这个这个parser.parse()到底干了什么
// 注意这个方法的返回对象 是Configuration 类对象
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
// 调用这个方法,解析配置文件
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
/**
* 解析配置文件
* 这里面可以看到配置文件中的一些标签,都是在这里进行解析的。
* 解析过程不算复杂,而且解析的方法都很类似。
*/
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
// 解析mapper配置文件
// 将mapper配置存储到Configuration的MapperRegistry容器中
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
执行完parser.parse()
之后,则得到了一个Configuration
对象接着执行
return build(parser.parse());
来看看这个SqlSessionFactoryBuilder
的build()
方法。
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
前面有提到由于SqlSessionFactory
是一个接口,而这里需要返回一个SqlSessionFactory
的实例对象,所以这个方法最终创建了一个DefaultSqlSessionFactory
对象作为返回结果。到此为止,SqlSessionFactory
创建完成。
接下一篇《SqlSession的创建和运行》
https://www.jianshu.com/p/04af4b3cc459
网友评论