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

上面列举了一些集合变量,这些变量用于缓存各种原信息。关于这些变量,这里描述的不太好懂,主要是不太好解释。要想了解这些变量更多的细节,还是要深入到源码中。所以我们成热打铁,继续往下分析。

● getter 方法解析过程

getter 方法解析的逻辑被封装在了addGetMethods方法中,这个方法除了会解析形如getXXX的方法,同时也会解析isXXX方法。该方法的源码分析如下:

private void addGetMethods(Class<?> cls) { Map<String, List<Method>> conflictingGetters = new HashMap<String, List<Method>>(); // 获取当前类,接口,以及父类中的方法。该方法逻辑不是很复杂,这里就不展开了 Method[] methods = getClassMethods(cls); for (Method method : methods) { // getter 方法不应该有参数,若存在参数,则忽略当前方法 if (method.getParameterTypes().length > 0) { continue; } String name = method.getName(); // 过滤出以 get 或 is 开头的方法 if ((name.startsWith("get") && name.length() > 3) || (name.startsWith("is") && name.length() > 2)) { // 将 getXXX 或 isXXX 等方法名转成相应的属性,比如 getName -> name name = PropertyNamer.methodToProperty(name); /* * 将冲突的方法添加到 conflictingGetters 中。考虑这样一种情况: * * getTitle 和 isTitle 两个方法经过 methodToProperty 处理, * 均得到 name = title,这会导致冲突。 * * 对于冲突的方法,这里先统一起存起来,后续再解决冲突 */ addMethodConflict(conflictingGetters, name, method); } } // 解决 getter 冲突 resolveGetterConflicts(conflictingGetters); }

如上,addGetMethods 方法的执行流程如下:

获取当前类,接口,以及父类中的方法

遍历上一步获取的方法数组,并过滤出以get和is开头的方法

将方法名转换成相应的属性名

将属性名和方法对象添加到冲突集合中

解决冲突

在上面的执行流程中,前三步比较简单,大家自行分析吧。第4步也不复杂,下面我会把源码贴出来,大家看一下就能懂。在这几步中,第5步逻辑比较复杂,这一步逻辑我们重点关注一下。下面继续看源码吧。

/** 添加属性名和方法对象到冲突集合中 */ private void addMethodConflict(Map<String, List<Method>> conflictingMethods, String name, Method method) { List<Method> list = conflictingMethods.get(name); if (list == null) { list = new ArrayList<Method>(); conflictingMethods.put(name, list); } list.add(method); } /** 解决冲突 */ private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) { for (Entry<String, List<Method>> entry : conflictingGetters.entrySet()) { Method winner = null; String propName = entry.getKey(); for (Method candidate : entry.getValue()) { if (winner == null) { winner = candidate; continue; } // 获取返回值类型 Class<?> winnerType = winner.getReturnType(); Class<?> candidateType = candidate.getReturnType(); /* * 两个方法的返回值类型一致,若两个方法返回值类型均为 boolean,则选取 isXXX 方法 * 为 winner。否则无法决定哪个方法更为合适,只能抛出异常 */ if (candidateType.equals(winnerType)) { if (!boolean.class.equals(candidateType)) { throw new ReflectionException( "Illegal overloaded getter method with ambiguous type for property " + propName + " in class " + winner.getDeclaringClass() + ". This breaks the JavaBeans specification and can cause unpredictable results."); /* * 如果方法返回值类型为 boolean,且方法名以 "is" 开头, * 则认为候选方法 candidate 更为合适 */ } else if (candidate.getName().startsWith("is")) { winner = candidate; } /* * winnerType 是 candidateType 的子类,类型上更为具体, * 则认为当前的 winner 仍是合适的,无需做什么事情 */ } else if (candidateType.isAssignableFrom(winnerType)) { /* * candidateType 是 winnerType 的子类,此时认为 candidate 方法更为合适, * 故将 winner 更新为 candidate */ } else if (winnerType.isAssignableFrom(candidateType)) { winner = candidate; } else { throw new ReflectionException( "Illegal overloaded getter method with ambiguous type for property " + propName + " in class " + winner.getDeclaringClass() + ". This breaks the JavaBeans specification and can cause unpredictable results."); } } // 将筛选出的方法添加到 getMethods 中,并将方法返回值添加到 getTypes 中 addGetMethod(propName, winner); } } private void addGetMethod(String name, Method method) { if (isValidPropertyName(name)) { getMethods.put(name, new MethodInvoker(method)); // 解析返回值类型 Type returnType = TypeParameterResolver.resolveReturnType(method, type); // 将返回值类型由 Type 转为 Class,并将转换后的结果缓存到 setTypes 中 getTypes.put(name, typeToClass(returnType)); } }

以上就是解除冲突的过程,代码有点长,不太容易看懂。这里大家只要记住解决冲突的规则即可理解上面代码的逻辑。相关规则如下:

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

冲突方法的返回值类型相同,如果返回值类型为boolean,那么以is开头的方法则是更合适的方法

冲突方法的返回值类型相同,但返回值类型非boolean,此时出现歧义,抛出异常

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

分析完 getter 方法的解析过程,下面继续分析 setter 方法的解析过程。

● setter 方法解析过程

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

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