了解这个类可以帮助你更深入的理解mybatis在解析Mapper方法参数时的原理。
主要属性// 统一的参数名称前缀
public static final String GENERIC_NAME_PREFIX = "param";
// 该布尔值用于标识是否使用实际参数名称,参数没有指定@Param注解时会置为true
private final boolean useActualParamName;
// 这个字段用于保存参数名称,使用SortedMap(默认会以key的自然顺序排序)来保存。
// key是索引值,value是参数名。如果指定了@Param,则使用指定的名称,否则使用索引值。
// 不过要注意的是,当参数列表里面有一些特殊参数(如RowBounds、ResultHandler)时,索引值与实际索引值会不一样。
// aMethod(@Param("M") int a, @Param("N") int b); 对应 {{0, "M"}, {1, "N"}}
// aMethod(int a, int b); 对应 {{0, "0"}, {1, "1"}}
// aMethod(int a, RowBounds rb, int b); 对应 {{0, "0"}, {2, "1"}}
private final SortedMap names;
// 该布尔值用来标识参数是否使用了@Param注解
private boolean hasParamAnnotation;
主要方法
ParamNameResolver构造方法getNamedParams方法 ParamNameResolver构造方法
该类的构造方法做了主要的参数名称解析工作。它能够将目标方法的参数名称依次列举出来。如果存在@Param注解,就会用该注解的value值替换参数名。
// Configuration类是mybatis的核心类,保存了所有配置相关信息。
// Method是需要被解析的Mapper方法。
public ParamNameResolver(Configuration config, Method method) {
// 将配置config对象的默认值true赋值给当前对象的useActualParamName属性。
this.useActualParamName = config.isUseActualParamName();
// 获取Mapper方法的参数类型,结果是一个Class对象数组。
final Class>[] paramTypes = method.getParameterTypes();
// 获取Mapper方法的参数注解,结果是一个Annotation接口的二维数组。
final Annotation[][] paramAnnotations = method.getParameterAnnotations();
// 用于存储所有参数名的SortedMap对象。
final SortedMap map = new TreeMap<>();
// 这个值用于判断接下来
int paramCount = paramAnnotations.length;
for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
// isSpecialParameter方法用于判断参数是否属于特殊参数类型
if (isSpecialParameter(paramTypes[paramIndex])) {
// 如果是会跳过。
continue;
}
// 判断参数的注解是否是@Param,如果是会将hasParamAnnotation设为true,并将注解的value值作为参数名称
String name = null;
for (Annotation annotation : paramAnnotations[paramIndex]) {
if (annotation instanceof Param) {
hasParamAnnotation = true;
name = ((Param) annotation).value();
break;
}
}
// 如果经过上面处理,参数名还是null,则说明当前参数没有指定@Param注解
if (name == null) {
if (useActualParamName) {
// 调用getActualParamName方法获取参数的实际名称。
name = getActualParamName(method, paramIndex);
}
// 如果参数名称还是为null,则将索引值作为参数名称。("0", "1", ...)
if (name == null) {
name = String.valueOf(map.size());
}
}
// 放入结果map
map.put(paramIndex, name);
}
// 最后使用Collections工具类的静态方法将结果map变为一个不可修改类型
names = Collections.unmodifiableSortedMap(map);
}
getNamedParams方法
该方法会将参数名和参数值对应起来,并且还会额外保存一份以param开头加参数顺序数字的值。
public Object getNamedParams(Object[] args) {
// 参数个数值
final int paramCount = names.size();
// 没有参数
if (args == null || paramCount == 0) {
return null;
// 有一个参数,并且没有@Param注解。
} else if (!hasParamAnnotation && paramCount == 1) {
Object value = args[names.firstKey()];
// 如果是Collection类型包装成Map类型。
return wrapToMapIfCollection(value, useActualParamName ? names.get(0) : null);
} else {
// 包装成ParamMap对象。这个对象继承了HashMap,重写了get方法。
final Map param = new ParamMap<>();
int i = 0;
// 将参数名作为key, 对应的参数值作为value,放入结果param对象中
for (Map.Entry entry : names.entrySet()) {
param.put(entry.getValue(), args[entry.getKey()]);
// 用于添加通用的参数名称,按顺序命名(param1, param2, ...)
final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);
// 确保不覆盖以@Param 命名的参数
if (!names.containsValue(genericParamName)) {
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}



