接上篇,继续来看看创建SqlSessionFactory对象的时候都做了些什么
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
Mybatis源码阅读,创建SqlSessionFactory对象都干了什么(1)
上篇文章主要看了一下configuration类中都有些什么东西,这篇文章就从.build()方法开始,一步一步往下看=>
直接从.build()方法进去看:
源码:
public SqlSessionFactory build(Reader reader) {
return build(reader, null, null);
}
传了一个reader对象和两个null给build(Reader reader, String environment, Properties properties)方法
源码:
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
//使用reader创建一个xml解析器
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
//parser.parse()的作用:将reader中的配置信息进行拆解并各节点数据放入一个configuration对象中
//即,parser.parse()最终得到一个包含配置文件中所有数据的configuration对象
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.
}
}
}
try中返回了 build(parser.parser()),finally中关闭reader
先来看看parse对象:
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
XMLConfigBuilder:
再看看它的父类BaseBuilder:
父类中有三个final的全局变量,configuration已经基本知道是用来存储配置信息的,那再分别看看TypeAliasRegistry和TypeHandlerRegistry
首先来看看TypeAliasRegistry
源码:
TypeAliasRegistry中创建了一个HashMap的final变量,可以看到构造方法中调用了很多次registerAlias方法,并且这个方法中传入的有Java基本数据类型String和数组等,并将其对应的类传入,那么来看看这个registerAlias方法:
public void registerAlias(String alias, Class<?> value) {
if (alias == null) {
throw new TypeException("The parameter alias cannot be null");
}
// issue #748
String key = alias.toLowerCase(Locale.ENGLISH);
if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + typeAliases.get(key).getName() + "'.");
}
typeAliases.put(key, value);
}
一句一句解释的话就是先对alias判空,非空的话将它转为英文小写,再到typeAliases中判断是否存在对应的key,存在的话判断这个key对应的value是否为空,如果不为空的话再判断这个value是不是和传入的value不一致,如果同时满足这三个条件则抛出异常,如果不同时满足的话就将它们放入typeAliases中,,整个的话可以理解为初始化一个map,这个map中存储的是数据类型。
再看看TypeHandlerRegistry:
源码:
先看一下这几个全局的final变量:
1、jdbcTypeHandlerMap:new的是EnumMap,这个东西我没有用过,但是从字面上来看的话“枚举Map”,嗯,后面还指定了一个JdbcType类,那么先看看这个JdbcType中装了些什么吧:
很熟悉的感觉吧,,原来JdbcType是一个枚举类,里面的这些个常量对应的就是数据库中的数据类型嘛,那么知道jdbcTypeHandlerMap这个map中的key就是这些常量了,再看看value是什么
TypeHandler<?>:
TypeHandler是一个接口,并且有很多的实现类,但是我们看它实现类的名字,都是数据库中的数据类型开头然后接上TypeHandler对吧,那么先暂时根据字面意思理解为数据库类型处理器吧(其实打开之后看了一下,发现看到后面有点看不懂)
好,我们知道jdbcTypeHandlerMap这个map中是以数据库中的数据类型为key存储的对应数据类型的处理器。 后面的几个就先不看了,tmd看是看了一下,描述起来太费力了
那么我们回到BaseBuilder这个类总结一下它的三个final全局变量都是个什么:
第一个:Configuration configuration; 用来存储配置信息的
第二个:TypeAliasRegistry typeAliasRegistry;用来存储数据类型的,其实就是
初始化一个Map<String, Class<?>>
第三个:TypeHandlerRegistry typeHandlerRegistry;用来存储对应数据库中数据类型的处理器
好的,我们到这看完了XMLConfigBuilder的父类BaseBuilder里面的几个final全局变量。。
我们接着看parse对象的创建:
public XMLConfigBuilder(Reader reader, String environment, Properties props) {
//XMLMapperEntityResolver(mybatis离线dtd文件解析器)、XPathParser对象(转换xml格式数据)
this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
}
我的注释中写明了XPathParser、XMLMapperEntityResolver都是干嘛的,但是还是跟进去看看吧,先来看XPathParser:
只是new了一个XMLMapperEntityResolver对象哈,里面也没有构造方法,那么就简单的看看它的变量,可以看到都是final修饰的String类型变量哈,“”中写的都是.dtd文件的名字
可以看到XMLMapperEntityResolver实现了一个接口EntityResolver,也不知道在什么地方通过这个接口来调用这个类中的方法,那么这个类中最重要的方法代码如下:
private InputSource getInputSource(String path, String publicId, String systemId) {
InputSource source = null;
if (path != null) {
try {
InputStream in = Resources.getResourceAsStream(path);
source = new InputSource(in);
source.setPublicId(publicId);
source.setSystemId(systemId);
} catch (IOException e) {
// ignore, null is ok
}
}
return source;
}
path就是前面定义的dtd文件路径,通过InputSource来加载对应的dtd文件。
好,看完了XMLMapperEntityResolver,那么接着看XPathParser:
调用了commonConstructor方法,再初始化了一下document这个变量,
先看看commonConstructor方法干了什么:
简单初始化了一下全局变量。
再看看createDocument(new InputSource(reader)):
private Document createDocument(InputSource inputSource) {
// important: this must only be called AFTER common constructor
//只能被命名为after common constructor
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
factory.setValidating(validation);
factory.setNamespaceAware(false);
factory.setIgnoringComments(true);
factory.setIgnoringElementContentWhitespace(false);
factory.setCoalescing(false);
factory.setExpandEntityReferences(true);
DocumentBuilder builder = factory.newDocumentBuilder();
builder.setEntityResolver(entityResolver);
builder.setErrorHandler(new ErrorHandler() {
@Override
public void error(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void fatalError(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void warning(SAXParseException exception) throws SAXException {
// NOP
}
});
return builder.parse(inputSource);
} catch (Exception e) {
throw new BuilderException("Error creating document instance. Cause: " + e, e);
}
}
这个就不细看了,总的就是使用xml格式来解析转化成输入流的reader对象,获取reader中的所有节点数据。
好,我们再回到这个地方:
已经看完了this里面的参数,那么看看this()方法中干了什么:
看看super()干了什么:
super()就是初始化了父类BaseBuilder中的几个全局变量。那么总的来说创建parser对象的时候就是以xml格式的方式解析了一下转换成输入流的reader,并且初始化了一下XMLConfigBuilder和BaseBuilder中的全局变量。
好,那么接着看parser.parse()又做了什么吧:
重点来了,,,parsed初始化时是false,这是为了保证这个方法只会被使用一次,parseConfiguration(parser.evalNode("/configuration"))用来解析配置文件中configuration标签所包含的所有标签的信息,本质就是根据配置文件来初始化configuration对象,并且这个解析过程只会解析一次
好,看最后的build:
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
根据已经初始化好的configuration的来创建一个DefaultSqlSessionFactory实例,
那么进去瞅瞅看:
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
创建SqlSessionFactory最终的一步:为DefaultSqlSessionFactory中的configuration进行初始化。
至此,创建SqlSessionFactory对象全过程解读完成。
最后,仅作为本人阅读源码笔记,若有错位还请指正,谢谢