XMLMapperBuilder###parameterMapElement()
//代码比较长了,因为parameterMap 涉及到比较多的东西
//
//
//
//
//
//
private ResultMap resultMapElement(XNode resultMapNode, List additionalResultMappings, Class> enclosingType) throws Exception {
//首先记录一下跟踪日志,有关ErrorContext后面会详细明说
ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
//这里可以看到用了一连串的嵌套,其实就是设置默认值而已
//简单解释这里的操作就是:首先获取`type`的值
//如果`type`为空就取`ofType`的值
//如果`ofType`为空就取`resultType`的值
//如果`resultType`为空就取`javaType`的值
String type = resultMapNode.getStringAttribute("type",
resultMapNode.getStringAttribute("ofType",
resultMapNode.getStringAttribute("resultType",
resultMapNode.getStringAttribute("javaType"))));
//解析这个类
Class> typeClass = resolveClass(type);
//如果type为null
//一般resolveClass只有在type为null的时候才会返回null
if (typeClass == null) {
//这里暂时没看懂,因为解析`ResultMap`传入的enclosingType为null
typeClass = inheritEnclosingType(resultMapNode, enclosingType);
}
Discriminator discriminator = null;
List resultMappings = new ArrayList<>();
resultMappings.addAll(additionalResultMappings);
//解析子节点
List resultChildren = resultMapNode.getChildren();
for (XNode resultChild : resultChildren) {
//单独处理构造函数节点
if ("constructor".equals(resultChild.getName())) {
//处理构造函数节点
processConstructorElement(resultChild, typeClass, resultMappings);
//单独处理鉴定器节点
} else if ("discriminator".equals(resultChild.getName())) {
discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
} else {
List flags = new ArrayList<>();
//如果子节点为,则将其保存起来,后续用来充当equals的作用
if ("id".equals(resultChild.getName())) {
flags.add(ResultFlag.ID);
}
//处理其他的子节点
resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
}
}
//获取此节点的id
String id = resultMapNode.getStringAttribute("id",
//不存在则自动生成一个唯一id
resultMapNode.getValueBasedIdentifier());
//获取继承节点信息
String extend = resultMapNode.getStringAttribute("extends");
//是否自动映射
Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
//创建解析类
ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
try {
//进行解析
return resultMapResolver.resolve();
} catch (IncompleteElementException e) {
configuration.addIncompleteResultMap(resultMapResolver);
throw e;
}
}
说实话,这段代码是在比较难理解,就光前面的嵌套获取type \ ofType \ jdbcType 等找了十万遍都没找到为什么。。甚至想到了是不是兼容老版本的问题。。可以看提交记录这些代码9年前都有了。。。
太难了
后来配合官方文档看,也能大概明白:
可以发现resultMap中可以嵌套
而中又可以嵌套其他的,比如 \
那如果要完整的解析这些嵌套的东西,最好的办法就是递归,
这就是为什么前面会有3中type的获取,因为这个方法不仅仅是在解析,还会被递归调用来解析 \ 等等。
先跳过其他的代码,我们先看看解析其他子节点
XMLMapperBuilder###buildResultMappingFromContext()
private ResultMapping buildResultMappingFromContext(XNode context, Class> resultType, List flags) throws Exception {
String property;
//判断时候需要通过构造方法赋值
if (flags.contains(ResultFlag.CONSTRUCTOR)) {
property = context.getStringAttribute("name");
} else {
//获取属性
property = context.getStringAttribute("property");
}
//获取其他属性
String column = context.getStringAttribute("column");
String javaType = context.getStringAttribute("javaType");
String jdbcType = context.getStringAttribute("jdbcType");
String nestedSelect = context.getStringAttribute("select");
String nestedResultMap = context.getStringAttribute("resultMap",
//处理嵌套的resultMap
processNestedResultMappings(context, Collections.emptyList(), resultType));
//继续获取其他的属性
String notNullColumn = context.getStringAttribute("notNullColumn");
String columnPrefix = context.getStringAttribute("columnPrefix");
String typeHandler = context.getStringAttribute("typeHandler");
String resultSet = context.getStringAttribute("resultSet");
String foreignColumn = context.getStringAttribute("foreignColumn");
boolean lazy = "lazy".equals(context.getStringAttribute("fetchType", configuration.isLazyLoadingEnabled() ? "lazy" : "eager"));
Class> javaTypeClass = resolveClass(javaType);
Class extends TypeHandler>> typeHandlerClass = resolveClass(typeHandler);
JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
//将这些属性通过解析助手进行解析
return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resultSet, foreignColumn, lazy);
}
这里最重要的便是处理嵌套的resultMap
一般来说,能被嵌套的元素有:
XMLMapperBuilder###processNestedResultMappings()
private String processNestedResultMappings(XNode context, List resultMappings, Class> enclosingType) throws Exception {
if ("association".equals(context.getName())
|| "collection".equals(context.getName())
|| "case".equals(context.getName())) {
//先不解析动态SQL
if (context.getStringAttribute("select") == null) {
//验证collection节点是否包含必须要元素
//必须包含resultMap 和 javaType其中一个
validateCollection(context, enclosingType);
//调用最开始的方法,进行递归解析
ResultMap resultMap = resultMapElement(context, resultMappings, enclosingType);
return resultMap.getId();
}
}
return null;
}
这里就能能看出,已经开始递归调用了
接下来,我们以 / 为主体元素,再次分析一遍resultMapElement的源代码
XMLMapperBuilder###parameterMapElement()
private ResultMap resultMapElement(XNode resultMapNode, List additionalResultMappings, Class> enclosingType) throws Exception {
ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
//对于resultMap 获取 type
//对于collection 获取 ofType 或 javaType
//对于association 获取 javaType
String type = resultMapNode.getStringAttribute("type",
resultMapNode.getStringAttribute("ofType",
resultMapNode.getStringAttribute("resultType",
resultMapNode.getStringAttribute("javaType"))));
//加载此type,可以指定别名
Class> typeClass = resolveClass(type);
if (typeClass == null) {
//这里enclosingType一般都是父节点的type
//比如
//
//那么enclosingType便是test
//如果有些节点没有配置type,允许的情况下,可以直接使用父节点的type
//例如:
//
//
//
// case节点中就没有配置type
typeClass = inheritEnclosingType(resultMapNode, enclosingType);
}
Discriminator discriminator = null;
List resultMappings = new ArrayList<>();
//添加已经解析过的resultMap
resultMappings.addAll(additionalResultMappings);
//继续解析子节点
List resultChildren = resultMapNode.getChildren();
for (XNode resultChild : resultChildren) {
//处理构造方法
if ("constructor".equals(resultChild.getName())) {
processConstructorElement(resultChild, typeClass, resultMappings);
}
//处理鉴定器
else if ("discriminator".equals(resultChild.getName())) {
discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
} else {
//处理其他子节点
List flags = new ArrayList<>();
if ("id".equals(resultChild.getName())) {
flags.add(ResultFlag.ID);
}
resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
}
}
//获取id
String id = resultMapNode.getStringAttribute("id",
resultMapNode.getValueBasedIdentifier());
String extend = resultMapNode.getStringAttribute("extends");
Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
//构建resultMap
ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
try {
//最后的解析
return resultMapResolver.resolve();
} catch (IncompleteElementException e) {
configuration.addIncompleteResultMap(resultMapResolver);
throw e;
}
}
接下来看其他细节:
当子节点解析完了以后,会将子节点添加到resultMapping中,然后再解析最大的resultMap
MapperBuilderAssiant###addResultMap()
//resultMapResolver.resolve();内部调用的此方法
public ResultMap addResultMap(
String id,
Class> type,
String extend,
Discriminator discriminator,
List resultMappings,
Boolean autoMapping) {
//首先给当前标签加上命名空间前缀
id = applyCurrentNamespace(id, false);
//然后给继承的标签加上命名空间前缀
//从这个当前前缀可以看出来,继承只能继承当前命名空间的元素
extend = applyCurrentNamespace(extend, true);
//判断是否有集成的属性
if (extend != null) {
//如果所继承的属性还没有解析,那么抛出指定异常,稍后再解析
if (!configuration.hasResultMap(extend)) {
throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'");
}
//通过继承找到对应的resultMap
ResultMap resultMap = configuration.getResultMap(extend);
//获取这个resultMap id 下对应的所有resultMap (包括嵌套resultMap)
List extendedResultMappings = new ArrayList<>(resultMap.getResultMappings());
//去除重复resultMap
extendedResultMappings.removeAll(resultMappings);
// Remove parent constructor if this resultMap declares a constructor.
boolean declaresConstructor = false;
//检查是否需要使用带参构造方法构造resultMap
for (ResultMapping resultMapping : resultMappings) {
if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
declaresConstructor = true;
break;
}
}
//如果需要使用构造方法创键`resultMap type`,那么将此需要使用构造方法的元素从继承元素中删除
if (declaresConstructor) {
extendedResultMappings.removeIf(resultMapping -> resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR));
}
resultMappings.addAll(extendedResultMappings);
}
//构造resultMap
ResultMap resultMap = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping)
.discriminator(discriminator)
.build();
configuration.addResultMap(resultMap);
return resultMap;
}
这几段代码看着实在累,第一是比较复杂,第二个就是本来想要实现的功能也比较复杂。
这里可以看见MyBatis仅仅是将嵌套的resultMap分解为几个reusltMap,然后放入list中,并且这里解析配置就真正的只是解析配置,没有做任何多余的事、
同时可以看出来,MyBatis的模块划分是非常好的,
XMLConfigBuilder->XMLMapperBuilder->MapperBuilderAssistant
XMLMapperBuilder只用负责读取配置文件,而将配置文件生成对像则交给MapperBuilderAssistant
看上面的代码,resultMap也只解析了静态部分能够解析的地方,而需要动态生成的则直接原封不动的放入了Configuration类中