概述
前几篇我们介绍了MyBatis的一些基本特性,对MyBatis有了个初步了解。接下来,我们将着手来分析一下MyBatis的源码,从源码层面复盘MyBatis的执行流程。
思维导图概括
配置文件解析过程分析
有了上述思维导图,我们对配置文件文件的解析过程就有了一个大概的认识,下面我们就来具体分析下解析过程。
配置文件解析入口
首先,我们来看看调用MyBatis的示例代码
String resource = "chapter1/mybatis-cfg.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
从上述示例代码中我们可以很清晰的看出,初始化过程是首先通过Resources 解析配置文件得到文件流。然后,将文件流传给SqlSessionFactoryBuilder的build方法,并最终得到sqlSessionFactory。
那么我们MyBatis的初始化入口就是SqlSessionFactoryBuilder的build 方法。
//* SqlSessionFactoryBuilder类
//以下3个方法都是调用下面第8种方法
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
//第8种方法和第4种方法差不多,Reader换成了InputStream
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
//最后一个build方法使用了一个Configuration作为参数,并返回DefaultSqlSessionFactory
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
从上述源码,我们可以知道build 构建SqlSessionFactory 分为两步,首先 实例化一个XMLConfigBuilder,然后,调用XMLConfigBuilder的parse方法得到Configuration对象,最后将Configuration对象作为参数实例化一个DefaultSqlSessionFactory 即SqlSessionFactory对象。
接着往下看,下面我们来看看XMLConfigBuilder类。首先是实例化XMLConfigBuilder的过程。
//* XMLConfigBuilder
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
//上面6个构造函数最后都合流到这个函数,传入XPathParser
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
//首先调用父类初始化Configuration
super(new Configuration());
//错误上下文设置成SQL Mapper Configuration(XML文件配置),以便后面出错了报错用吧
ErrorContext.instance().resource("SQL Mapper Configuration");
//将Properties全部设置到Configuration里面去
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
//* XPathParser
public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) {
commonConstructor(validation, variables, entityResolver);
this.document = createDocument(new InputSource(reader));
}
private Document createDocument(InputSource inputSource) {
// important: this must only be called AFTER common constructor
try {
//这个是DOM解析方式
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(validation);
//名称空间
factory.setNamespaceAware(false);
//忽略注释
factory.setIgnoringComments(true);
//忽略空白
factory.setIgnoringElementContentWhitespace(false);
//把 CDATA 节点转换为 Text 节点
factory.setCoalescing(false);
//扩展实体引用
factory.setExpandEntityReferences(true);
DocumentBuilder builder = factory.newDocumentBuilder();
//需要注意的就是定义了EntityResolver(XMLMapperEntityResolver),这样不用联网去获取DTD,
//将DTD放在org\apache\ibatis\builder\xml\mybatis-3-config.dtd,来达到验证xml合法性的目的
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 {
}
});
return builder.parse(inputSource);
} catch (Exception e) {
throw new BuilderException("Error creating document instance. Cause: " + e, e);
}
}
从上述源码中,我们可以看出在XMLConfigBuilder的实例化过程包括两个过程,1. 创建XPathParser的实例并初始化;2.创建Configuration的实例对象,然后将XPathParser的实例设置到XMLConfigBuilder中,而XPathParser 初始化主要做了两件事,初始化DocumentBuilder对象,并通过调用DocumentBuilder对象的parse方法得到Document对象,我们配置文件的配置就全部都转移到了Document对象中。我们下面通过调试看看Document 对象中的内容,测试用例是MyBatis 自身的单元测试XPathParserTest
。。。。。。。。。。。。。。。。。
版权原因,完整文章,请参考如下: