Mybatis源码分析(四):属性接口之configLocation

版权声明:欢迎转载,注明出处即可,谢谢! https://blog.csdn.net/qq_17231297/article/details/84566993

上节内容我们了解了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
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_17231297/article/details/84566993