Mybatis配置文件解析
配置文件解析入口
不通过spring框架集成单独使用mybatis,第一步是读取配置文件构建SqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream ins = Resource.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlsessionFactoryBuilder().build(ins);
那么mybatis是如何解析xml配置的呢, 我们根据输入流逐层跟踪
//XMLConfigBuilder
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.
}
}
}
//XPathParser
public XMLConfigBuilder(Reader reader, String environment, Properties props) {
this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
}
//XPathParser
public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) {
commonConstructor(validation, variables, entityResolver);
this.document = createDocument(new InputSource(reader));
}
//使用DocumentBuilderFactory读取xml
private Document createDocument(InputSource inputSource) {
// important: this must only be called AFTER common constructor
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
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 {
}
});
return builder.parse(inputSource);
} catch (Exception e) {
throw new BuilderException("Error creating document instance. Cause: " + e, e);
}
}
因此我们可知对xml的解析还是基于JDK的jaxp。
常用java解析xml方式
方式 | 简介|优点|缺点
---|---|---|---|---
DOM解析|以层次结构(类似于树型)来组织节点和信息片段,映射XML文档的结构,允许获取和操作文档的任意部分,是W3C的官方标准|①允许应用程序对数据和结构做出更改②访问是双向的,可以在任何时候在树中上下导航,获取和操作任意部分的数据。|通常需要加载整个XML文档来构造层次结构,消耗资源大
SAX解析| 流模型中的”推”模型分析方式。通过事件驱动,每发现一个节点就引发一个事件,事件推给事件处理器,通过回调方法完成解析工作,解析XML文档的逻辑需要应用程序完成|①不需要等待所有数据都被处理,分析就能立即开始②只在读取数据时检查数据,不需要保存在内存中③可以在某个条件得到满足时停止解析,不必解析整个文档④效率和性能较高,能解析大于系统内存的文档。|①需要应用程序自己负责TAG的处理逻辑(例如维护父/子关系等),文档越复杂程序就越复杂②单向导航,无法定位文档层次,很难同时访问同一文档的不同部分数据,不支持XPath。
JDOM|Java特定的文档对象模型,自身不包含解析器,使用SAX|①使用具体类而不是接口,简化了DOM的API②大量使用了Java集合类,方便了Java开发人员|①没有较好的灵活性②性能较差
DOM4J|简单易用,采用Java集合框架,并完全支持DOM、SAX和JAXP|①大量使用了Java集合类,方便Java开发人员,同时提供一些提高性能的替代方法②支持XPath③有很好的性能|大量使用了接口,API较为复杂
解析流程核心方法
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"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
这里面有十几个方法,对应着config文件里面的所有一级标签。方法简介如下:
propertiesElement()
解析properties标签,读取我们引入的外部配置文件。这里有两种类型,一种是放在resource目录下的,是相对路径,一种是写的绝对路径。解析的最终结果就是我们会把所有的配置信息放到名为defaults的Properties对象里面,最后把XPathParser和Configuration的Properties属性都设置成我们填充后的Properties对象。
settingsAsProperties()
把settings标签也解析成了Properties对象,对于
loadCustomVfs(settings)
loadCustomVfs是获取Vitual File System的自定义实现类,比如我们要读取本地文件,或者FTP远程文件的时候,就可以用到自定义的VFS类。我们根据
loadCustomLogImpl(settings)
loadCustomLogImpl是根据
typeAliasesElement()
接下来,解析
pluginElement()
接下来解析的是
objectFactoryElement()、objectWrapperFactoryElement()
这两个标签是用来实例化对象用的,
reflectorFactory()
解析
settingsElement(settings)
这里就是对
environmentsElement()
这一步解析
databaseIdProviderElement()
解析
typeHandlerElement()
跟typeAlias一样,TypeHandler有两种配置方式,一种是单独配置一个类,一种是指定一个package。最后我们得到的是JavaType和JdbcType,以及用来做相互映射的TypeHandler之间的映射关系。最后放在TypeHandlerRegistry对象里面