http://www.cnblogs.com/question-sky/p/6612604.html
默认加载mybatis主文件方式
XMLConfigBuilder xmlConfigBuilder = null;
if (this.configLocation != null) {
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();
} else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Property 'configLocation' not specified, using default MyBatis Configuration");
}
//sqlSessionFactoryBean不指定configLocation属性则采用默认的Configuration对象
configuration = new Configuration();
configuration.setVariables(this.configurationProperties);
}
简单的看下Configuration
的无参数构造函数
public Configuration() {
//预存常用的别名
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
typeAliasRegistry.registerAlias("LRU", LruCache.class);
typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
languageRegistry.register(RawLanguageDriver.class);
}
简单的发现其并不去设置前一章节的mybatis主文件中的相关属性,比如settings、environment等
mapper sql 配置文件的加载
前提是
sqlSessionFactoryBean
设置了mapperLocations属性,比如<property name="mapperLocations" value="classpath:com/du/wxServer/mapper/*.xml" />
-
查看
sqlSessionFactoryBean#buildSqlSessionFactory()
方法if (!isEmpty(this.mapperLocations)) { //具体的如何从string转为Resource[],暂且不知何处加载获得,有兴趣的读者可补充 for (Resource mapperLocation : this.mapperLocations) { if (mapperLocation == null) { continue; } try { //对扫描包及其子包下的每个sql mapper配置文件进行解析 XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), configuration, mapperLocation.toString(), configuration.getSqlFragments()); xmlMapperBuilder.parse(); } catch (Exception e) { throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e); } finally { ErrorContext.instance().reset(); } if (this.logger.isDebugEnabled()) { this.logger.debug("Parsed mapper file: '" + mapperLocation + "'"); } } } else { if (this.logger.isDebugEnabled()) { this.logger.debug("Property 'mapperLocations' was not specified or no matching resources found"); } }
-
直接进入
XMLMapperBuilder#parse
方法public void parse() { //对每个xml资源只加载一次 if (!configuration.isResourceLoaded(resource)) { //解析xml配置,其中配置的根节点必须为mapper configurationElement(parser.evalNode("/mapper")); configuration.addLoadedResource(resource); //绑定mapper的工作区间 bindMapperForNamespace(); } parsePendingResultMaps(); parsePendingChacheRefs(); parsePendingStatements(); }
-
分析
XMLMapperBuilder#configurationElement
方法
这里讨论下与前一章节不同的解析同一属性配置方法:try { //表明mapper根节点的namespace属性是必须的,且不为空 String namespace = context.getStringAttribute("namespace"); if (namespace.equals("")) { throw new BuilderException("Mapper's namespace cannot be empty"); } //设置工作区间 builderAssistant.setCurrentNamespace(namespace); //解析相应的属性 cacheRefElement(context.evalNode("cache-ref")); cacheElement(context.evalNode("cache")); //解析<parameterMap>节点集合 parameterMapElement(context.evalNodes("/mapper/parameterMap")); //解析<resultMap>节点集合 resultMapElements(context.evalNodes("/mapper/resultMap")); //解析<sql>节点集合 sqlElement(context.evalNodes("/mapper/sql")); //创建MappedStatement,这里与注解方式的加载方式还是类似的 buildStatementFromContext(context.evalNodes("select|insert|update|delete")); } catch (Exception e) { throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e); }
-
XMLMapperBuilder#resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings)
解析单个<resultMap>
节点方法ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier()); //读取id属性,最好配置以免不必要的错误 String id = resultMapNode.getStringAttribute("id", resultMapNode.getValueBasedIdentifier()); //优先级为type>ofType>resultType>javaType String type = resultMapNode.getStringAttribute("type", resultMapNode.getStringAttribute("ofType", resultMapNode.getStringAttribute("resultType", resultMapNode.getStringAttribute("javaType")))); String extend = resultMapNode.getStringAttribute("extends"); //是否开启自动映射,默认值为unset Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping"); Class<?> typeClass = resolveClass(type); //<discriminator><case /><case/></discriminator>根据结果值进行结果类型的映射,类似java的switch-case语法 Discriminator discriminator = null; //ResultMap节点信息转化为ResultMapping集合 List<ResultMapping> resultMappings = new ArrayList<ResultMapping>(); resultMappings.addAll(additionalResultMappings); List<XNode> resultChildren = resultMapNode.getChildren(); for (XNode resultChild : resultChildren) { if ("constructor".equals(resultChild.getName())) { //<resultMap>节点下<constructor>节点处理 processConstructorElement(resultChild, typeClass, resultMappings); } else if ("discriminator".equals(resultChild.getName())) { //<resultMap>节点下<discriminator>节点处理 discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings); } else { //<id>/<result>/<collection>/<association>节点的解析 ArrayList<ResultFlag> flags = new ArrayList<ResultFlag>(); if ("id".equals(resultChild.getName())) { flags.add(ResultFlag.ID); } resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags)); } } ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping); try { //组装成ResultMap对象保存到Configuration对象的私有集合变量resultMaps return resultMapResolver.resolve(); } catch (IncompleteElementException e) { configuration.addIncompleteResultMap(resultMapResolver); throw e; }
-
XMLMapperBuilder#sqlElement(List<XNode list>)
sql节点信息的解析,主要作用是将每个sql节点对象都保存到Configuration
对象中的Map<String, XNode> sqlFragments
属性中。for (XNode context : list) { //sql节点的databaseId属性 String databaseId = context.getStringAttribute("databaseId"); //sql节点的id属性 String id = context.getStringAttribute("id"); //id=${namespace}+"."+id id = builderAssistant.applyCurrentNamespace(id, false); //true的前提是 //主配置文件指定了databaseId属性 //或者主配置和sql节点的databaseId属性均不存在,但sql节点的id属性存在 if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) { sqlFragments.put(id, context); } }
-
XMLMapperBuilder#buildStatementFromContext(context.evalNodes("select|insert|update|delete"))
CRUD语句节点解析,
这里直接看XMLStatementBuilder#parseStatementNode()
方法的部分代码//节点上支持的常见属性 Integer fetchSize = context.getIntAttribute("fetchSize"); Integer timeout = context.getIntAttribute("timeout"); String parameterMap = context.getStringAttribute("parameterMap"); String parameterType = context.getStringAttribute("parameterType"); Class<?> parameterTypeClass = resolveClass(parameterType); String resultMap = context.getStringAttribute("resultMap"); String resultType = context.getStringAttribute("resultType"); String lang = context.getStringAttribute("lang"); LanguageDriver langDriver = getLanguageDriver(lang); ... String resultSetType = context.getStringAttribute("resultSetType"); StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString())); ... // Include Fragments before parsing 导入<include>标签内容,其内部可以含有<if>/<where>/<set>等标签 XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant); includeParser.applyIncludes(context.getNode()); // Parse selectKey after includes and remove them.导入<selectKey>标签内容 processSelectKeyNodes(id, parameterTypeClass, langDriver); // Parse the SQL (pre: <selectKey> and <include> were parsed and removed) 如何解析sql语句?放置下一章节讲解 SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass); ... //这里就跟上一章节的注解生成MappedStatement是一致的,最终都是保存在Configuration的Map<String, MappedStatement> mappedStatement集合属性 builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
-
总结
- 不管是通过注解模式还是配置文件模式,都会生成MappedStatement对象保存到Configuration对象中
- 注解模式可以很好的直接通过上一章节的讲解模式来达到sql语句与类直接绑定;但本章的sql 配置文件并没有讲到如何绑定对应namespace指向的class对象。这在
MapperScannerConfigurer源码分析
中讲解 - 每个
select|update|insert|delete
标签均会被解析为单个MappedStatement对象,其中的id为namespace_id
作为唯一标志
下节预告
Spring mybatis源码篇章-XMLLanguageDriver解析sql包装为SqlSource