上节内容我们了解了SqlSessionFactoryBean所有暴露出来的属性,方便用户通过配置对mybatis扩展!今天我们一起看看configLocation源码!
对configLocation进行源码分析我就的知道这个属性在spring和mybatis整合的时候怎么使用!
一.configLocation如何使用
Spring核心配置文件中的配置如下(SqlSessionFactoryBean配置configLocation属性,去加载mybatis的config配置文件)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<typeAlias type="com.zzy.model.User" alias="User" />
</typeAliases>
<mappers>
<mapper resource="com/zzy/xml/UserMapper.xml" />
</mappers>
<!--
<plugins>
<plugin interceptor="com.zzy.intercept.SQLStatsInterceptor"></plugin>
</plugins>
<databaseIdProvider type="org.apache.ibatis.mapping.VendorDatabaseIdProvider"></databaseIdProvider>
-->
</configuration>
这里标签属性实际上和上节SqlSessionFactoryBean 下属性是一致的。暂时不做详细配置!(也就是可以把SqlSessionFactoryBean下属性全部配置到mybatis配置文件中)
二.源码分析过程
1.configLocation 的入口源码
先看这个属性被mybatis读取位置的源码
SqlSessionFactoryBean >> buildSqlSessionFactory
XMLConfigBuilder xmlConfigBuilder = null;
if (this.configuration != null) {
configuration = this.configuration;
if (configuration.getVariables() == null) {
configuration.setVariables(this.configurationProperties);
} else if (this.configurationProperties != null) {
configuration.getVariables().putAll(this.configurationProperties);
}
} else if (this.configLocation != null) {
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
}
configuration = new Configuration();
if (this.configurationProperties != null) {
configuration.setVariables(this.configurationProperties);
}
}
上述代码含义Mybatis初始化的过程,首先看configuration是否为空,初始化之前这个对象是为空的。
然后就是看是否配置了configLocation 。
如果配置了,会将configLocation包装成一个xmlConfigBuilder对象。
有必要先说明下org.apache.ibatis.session.Configuration
和org.apache.ibatis.builder.xml.XMLConfigBuilder这两个类
Configuration:
mybatis的一个数据管家,里面存在了,只要mybatis执行过程中用到的各种数据都在,例如:mapper文件路径、别名数据、解析出来的sql语句、结果集数据等)这个类在mybatis框架中比较总要的一个类,下期着重讲!
XMLConfigBuilder:
public class XMLConfigBuilder extends BaseBuilder
用来解析mybatis的全局配置文件sqlMapConfig.xml的,这个类继承自父类BaseBuilder,父类中有个很重要的方法parse(代码如下:)
else if (this.configLocation != null) {
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();
}
2.xmlConfigBuilder 解析sqlMapConfig.xml过程
a.
上面sqlMapConfig.xml被读取到后,创建了xmlConfigBuilder对象,然后是用xmlConfigBuilder中的方法解析xml文件,继续看buildSqlSessionFactory这个方法。xml解析的位置在这个方法中靠下的位置有如下代码:
if (xmlConfigBuilder != null) {
try {
xmlConfigBuilder.parse();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
}
} catch (Exception ex) {
throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
} finally {
ErrorContext.instance().reset();
}
}
xmlConfigBuilder.parse();就是解析读取到的mybatis配置文件
sqlMapConfig.xml。
b.
XmlConfigBuilder 的parse方法源码:
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
其中parseConfiguration(parser.evalNode("/configuration"));
这段代码简单说下,parseConfiguration深入解析的方法。
parser.evalNode("/configuration")属于XPath 语法【自己复习】,这个地方的含义就是选取configuration为根节点,我们可以看到
parser.evalNode("/configuration")返回值是
sqlMapConfig.xml文件中configuration就是根节点,获取到
configuration根节点的全部信息,断点截图如下:
c.
parseConfiguration() 源码如下:
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);
}
}
上面代码就是对sqlMapConfig.xml文件中的标签依次读取(就是上节讲到的mybatis所暴露出的所有接口)Mybatis源码分析(三):Mybatis所有暴露的接口及其作用。
举两个元素解读的例子:xml文件中两个标签(typeAliases和mappers)的解析方法
typeAliasesElement(root.evalNode(“typeAliases”));
mapperElement(root.evalNode(“mappers”));
简单说明下typeAliases标签的解析过程,除去mappers标签之外,其他标签就不做详细解读(类似typeAliases)。
typeAliases标签的解析过程源码如下:
private void typeAliasesElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String typeAliasPackage = child.getStringAttribute("name");
configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
} else {
String alias = child.getStringAttribute("alias");
String type = child.getStringAttribute("type");
try {
Class<?> clazz = Resources.classForName(type);
if (alias == null) {
typeAliasRegistry.registerAlias(clazz);
} else {
typeAliasRegistry.registerAlias(alias, clazz);
}
} catch (ClassNotFoundException e) {
throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
}
}
}
}
}
上面整个过程就是对解析的过程
上面代码中我们可以看到一个对象typeAliasRegistry,这个对象反射类是
TypeAliasRegistry,这个类是用来包装别名存在的map
简单看看TypeAliasRegistry.class:
public class TypeAliasRegistry {
private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();
其中TYPE_ALIASES存放别名对应关系的一个map,这个map又被typeAliasRegistry封装。
上面代码typeAliasRegistry.registerAlias(alias, clazz)就是讲type和alias的对应关系注册到typeAliasRegistry对象的map中!
以上所有的操作过程都在XMLConfigBuilder中,在XMLConfigBuilder父类BaseBuilder中有这样一段代码
this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
最后typeAliasRegistry 别名对象还是被放到大数据管家configuration中了(下节分析configuration),验证了我们上面说的configuration包含mybatis所需的各种数据。
d.
mapperElement(root.evalNode(“mappers”))源码中截取重要的一段
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
}
关键方法 mapperParser.parse()代码:
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingChacheRefs();
parsePendingStatements();
}
这个方法是对Mapper.xml文件中增删改查标签解析的过程。
多说一句,通过mapperLocation这个属性配置Mapper.xml文件,解析Mapper.xml也会回到上面方法中(后期分析mapperLocation属性接口的
时候会通过源码看到)
欢迎大家加入Java高级架构/互联网(严禁培训机构、广告群,最干净的技术交流群):微信号:1083683150(备注:技术群或者4000G架构师资源)
微信平台本人收集个大量资源(4000G架构师资源,1000G大数据资源),只做分享,欢迎大家关注获取,保证免费,非任何机构
更多精彩请扫码关注微信公众号—— 名称:java版web项目 id :java_project