分享

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

 云梦天涯rqxe9h 2018-02-14

上节内容我们了解了SqlSessionFactoryBean所有暴露出来的属性,方便用户通过配置对mybatis扩展!今天我们一起看看configLocation源码!

 

对configLocation进行源码分析我就的知道这个属性在spring和mybatis整合的时候怎么使用!

一.configLocation如何使用

Spring核心配置文件中的配置如下(SqlSessionFactoryBean配置configLocation属性,去加载mybatis的config配置文件

'sqlSessionFactory' class='org.mybatis.spring.SqlSessionFactoryBean'>
   property name='dataSource' ref='dataSource' />
   
   property name='configLocation'>
     value>classpath:sqlMapConfig.xmlvalue>
   property>
bean>


sqlMapConfig.xml

<>xml version='1.0' encoding='UTF-8' ?>  
PUBLIC '-////DTD Config 3.0//EN'  
'http:///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>
 
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文件中两个标签(typeAliasesmappers)的解析方法

  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);
         }
       }
     }
   }
 }


上面整个过程就是对typeAlias type='com.zzy.model.User' alias='User' />解析的过程

上面代码中我们可以看到一个对象typeAliasRegistry,这个对象反射类是

TypeAliasRegistry,这个类是用来包装别名存在的map

 

简单看看TypeAliasRegistry.class:

public class TypeAliasRegistry {

 private final MapString, Class> TYPE_ALIASES = new HashMapString, 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属性接口的

时候会通过源码看到)

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多