上节内容我们了解了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文件中两个标签(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); } } } } }
上面整个过程就是对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属性接口的 时候会通过源码看到)
|