|
上節(jié)內(nèi)容我們了解了SqlSessionFactoryBean所有暴露出來(lái)的屬性,方便用戶通過(guò)配置對(duì)mybatis擴(kuò)展!今天我們一起看看configLocation源碼! 對(duì)configLocation進(jìn)行源碼分析我就的知道這個(gè)屬性在spring和mybatis整合的時(shí)候怎么使用! 一.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>
這里標(biāo)簽屬性實(shí)際上和上節(jié)SqlSessionFactoryBean 下屬性是一致的。暫時(shí)不做詳細(xì)配置?。ㄒ簿褪强梢园裇qlSessionFactoryBean下屬性全部配置到mybatis配置文件中)
二.源碼分析過(guò)程
1.configLocation 的入口源碼
先看這個(gè)屬性被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初始化的過(guò)程,首先看configuration是否為空,初始化之前這個(gè)對(duì)象是為空的。
然后就是看是否配置了configLocation 。
如果配置了,會(huì)將configLocation包裝成一個(gè)xmlConfigBuilder對(duì)象。
有必要先說(shuō)明下org.apache.ibatis.session.Configuration 和org.apache.ibatis.builder.xml.XMLConfigBuilder這兩個(gè)類 Configuration: mybatis的一個(gè)數(shù)據(jù)管家,里面存在了,只要mybatis執(zhí)行過(guò)程中用到的各種數(shù)據(jù)都在,例如:mapper文件路徑、別名數(shù)據(jù)、解析出來(lái)的sql語(yǔ)句、結(jié)果集數(shù)據(jù)等)這個(gè)類在mybatis框架中比較總要的一個(gè)類,下期著重講! XMLConfigBuilder: public class XMLConfigBuilder extends BaseBuilder 用來(lái)解析mybatis的全局配置文件sqlMapConfig.xml的,這個(gè)類繼承自父類BaseBuilder,父類中有個(gè)很重要的方法parse(代碼如下:) else if (this.configLocation != null) { xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties); configuration = xmlConfigBuilder.getConfiguration(); }
2.xmlConfigBuilder 解析sqlMapConfig.xml過(guò)程
a. 上面sqlMapConfig.xml被讀取到后,創(chuàng)建了xmlConfigBuilder對(duì)象,然后是用xmlConfigBuilder中的方法解析xml文件,繼續(xù)看buildSqlSessionFactory這個(gè)方法。xml解析的位置在這個(gè)方法中靠下的位置有如下代碼:
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')); 這段代碼簡(jiǎn)單說(shuō)下,parseConfiguration深入解析的方法。 parser.evalNode('/configuration')屬于XPath 語(yǔ)法【自己復(fù)習(xí)】,這個(gè)地方的含義就是選取configuration為根節(jié)點(diǎn),我們可以看到parser.evalNode('/configuration')返回值是 sqlMapConfig.xml文件中configuration就是根節(jié)點(diǎn),獲取到
configuration根節(jié)點(diǎn)的全部信息,斷點(diǎn)截圖如下:

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); } }
上面代碼就是對(duì)sqlMapConfig.xml文件中的標(biāo)簽依次讀取(就是上節(jié)講到的mybatis所暴露出的所有接口)Mybatis源碼分析(三):Mybatis所有暴露的接口及其作用。
舉兩個(gè)元素解讀的例子:xml文件中兩個(gè)標(biāo)簽(typeAliases和mappers)的解析方法 typeAliasesElement(root.evalNode('typeAliases')); mapperElement(root.evalNode('mappers')); 簡(jiǎn)單說(shuō)明下typeAliases標(biāo)簽的解析過(guò)程,除去mappers標(biāo)簽之外,其他標(biāo)簽就不做詳細(xì)解讀(類似typeAliases)。
typeAliases標(biāo)簽的解析過(guò)程源碼如下:
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); } } } } }
上面整個(gè)過(guò)程就是對(duì)typeAlias type='com.zzy.model.User' alias='User' />解析的過(guò)程 上面代碼中我們可以看到一個(gè)對(duì)象typeAliasRegistry,這個(gè)對(duì)象反射類是 TypeAliasRegistry,這個(gè)類是用來(lái)包裝別名存在的map 簡(jiǎn)單看看TypeAliasRegistry.class: public class TypeAliasRegistry {
private final MapString, Class?>> TYPE_ALIASES = new HashMapString, Class?>>();
其中TYPE_ALIASES存放別名對(duì)應(yīng)關(guān)系的一個(gè)map,這個(gè)map又被typeAliasRegistry封裝。 上面代碼typeAliasRegistry.registerAlias(alias, clazz)就是講type和alias的對(duì)應(yīng)關(guān)系注冊(cè)到typeAliasRegistry對(duì)象的map中! 以上所有的操作過(guò)程都在XMLConfigBuilder中,在XMLConfigBuilder父類BaseBuilder中有這樣一段代碼 this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
最后typeAliasRegistry 別名對(duì)象還是被放到大數(shù)據(jù)管家configuration中了(下節(jié)分析configuration),驗(yàn)證了我們上面說(shuō)的configuration包含mybatis所需的各種數(shù)據(jù)。
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(); }
關(guān)鍵方法 mapperParser.parse()代碼: public void parse() { if (!configuration.isResourceLoaded(resource)) { configurationElement(parser.evalNode('/mapper')); configuration.addLoadedResource(resource); bindMapperForNamespace(); }
parsePendingResultMaps(); parsePendingChacheRefs(); parsePendingStatements(); }
這個(gè)方法是對(duì)Mapper.xml文件中增刪改查標(biāo)簽解析的過(guò)程。
多說(shuō)一句,通過(guò)mapperLocation這個(gè)屬性配置Mapper.xml文件,解析Mapper.xml也會(huì)回到上面方法中(后期分析mapperLocation屬性接口的 時(shí)候會(huì)通過(guò)源碼看到)
|