MyBatis 源码分析 - 配置文件解析过程 (6)

与 getter 方法解析过程相比,setter 方法的解析过程与此有一定的区别。主要体现在冲突出现的原因,以及冲突的解决方法上。那下面,我们深入源码来找出两者之间的区别。

private void addSetMethods(Class<?> cls) { Map<String, List<Method>> conflictingSetters = new HashMap<String, List<Method>>(); // 获取当前类,接口,以及父类中的方法。该方法逻辑不是很复杂,这里就不展开了 Method[] methods = getClassMethods(cls); for (Method method : methods) { String name = method.getName(); // 过滤出 setter 方法,且方法仅有一个参数 if (name.startsWith("set") && name.length() > 3) { if (method.getParameterTypes().length == 1) { name = PropertyNamer.methodToProperty(name); /* * setter 方法发生冲突原因是:可能存在重载情况,比如: * void setSex(int sex); * void setSex(SexEnum sex); */ addMethodConflict(conflictingSetters, name, method); } } } // 解决 setter 冲突 resolveSetterConflicts(conflictingSetters); }

从上面的代码和注释中,我们可知道 setter 方法之间出现冲突的原因。即方法存在重载,方法重载导致methodToProperty方法解析出的属性名完全一致。而 getter 方法之间出现冲突的原因是getXXX和isXXX对应的属性名一致。既然冲突发生了,要进行调停,那接下来继续来看看调停冲突的逻辑。

private void resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) { for (String propName : conflictingSetters.keySet()) { List<Method> setters = conflictingSetters.get(propName); /* * 获取 getter 方法的返回值类型,由于 getter 方法不存在重载的情况, * 所以可以用它的返回值类型反推哪个 setter 的更为合适 */ Class<?> getterType = getTypes.get(propName); Method match = null; ReflectionException exception = null; for (Method setter : setters) { // 获取参数类型 Class<?> paramType = setter.getParameterTypes()[0]; if (paramType.equals(getterType)) { // 参数类型和返回类型一致,则认为是最好的选择,并结束循环 match = setter; break; } if (exception == null) { try { // 选择一个更为合适的方法 match = pickBetterSetter(match, setter, propName); } catch (ReflectionException e) { match = null; exception = e; } } } // 若 match 为空,表示没找到更为合适的方法,此时抛出异常 if (match == null) { throw exception; } else { // 将筛选出的方法放入 setMethods 中,并将方法参数值添加到 setTypes 中 addSetMethod(propName, match); } } } /** 从两个 setter 方法中选择一个更为合适方法 */ private Method pickBetterSetter(Method setter1, Method setter2, String property) { if (setter1 == null) { return setter2; } Class<?> paramType1 = setter1.getParameterTypes()[0]; Class<?> paramType2 = setter2.getParameterTypes()[0]; // 如果参数2可赋值给参数1,即参数2是参数1的子类,则认为参数2对应的 setter 方法更为合适 if (paramType1.isAssignableFrom(paramType2)) { return setter2; // 这里和上面情况相反 } else if (paramType2.isAssignableFrom(paramType1)) { return setter1; } // 两种参数类型不相关,这里抛出异常 throw new ReflectionException("Ambiguous setters defined for property '" + property + "' in class '" + setter2.getDeclaringClass() + "' with types '" + paramType1.getName() + "' and '" + paramType2.getName() + "'."); } private void addSetMethod(String name, Method method) { if (isValidPropertyName(name)) { setMethods.put(name, new MethodInvoker(method)); // 解析参数类型列表 Type[] paramTypes = TypeParameterResolver.resolveParamTypes(method, type); // 将参数类型由 Type 转为 Class,并将转换后的结果缓存到 setTypes setTypes.put(name, typeToClass(paramTypes[0])); } }

关于 setter 方法冲突的解析规则,这里也总结一下吧。如下:

冲突方法的参数类型与 getter 的返回类型一致,则认为是最好的选择

冲突方法的参数类型具有继承关系,子类参数对应的方法被认为是更合适的选择

冲突方法的参数类型不相关,无法确定哪个是更好的选择,此时直接抛异常

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyzyxw.html