【Mybatis】【基础设施】【二】Mybatis源码解析-Reflector类信息解析者
1 前言
上一节我们分析了一个基础设置的PropertyTokenizer,这节我们分析下Reflector,它是Mybatis对于一个类的信息收集者,也就是解析类的相关信息放到自己的集合里,对于它的管理有一个ReflectorFactory,会对他进行缓存和管理,ReflectorFactory是一个接口,默认的实现是DefaultReflectorFactory,那么我们本节就分析下Reflector是怎么收集类的信息的。
public class DefaultReflectorFactory implements ReflectorFactory { private boolean classCacheEnabled = true; // 缓存 private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<>(); public DefaultReflectorFactory() { } @Override public Reflector findForClass(Class<?> type) { // 是否开启缓存 if (classCacheEnabled) { // 有就从缓存中拿, 没有就创建 return reflectorMap.computeIfAbsent(type, Reflector::new); } else { return new Reflector(type); } } }
2 源码分析
2.1 方法通读
public class Reflector { private final Class<?> type; private final String[] readablePropertyNames; private final String[] writablePropertyNames; private final Map<String, Invoker> setMethods = new HashMap<>(); private final Map<String, Invoker> getMethods = new HashMap<>(); private final Map<String, Class<?>> setTypes = new HashMap<>(); private final Map<String, Class<?>> getTypes = new HashMap<>(); private Constructor<?> defaultConstructor; private Map<String, String> caseInsensitivePropertyMap = new HashMap<>(); public Reflector(Class<?> clazz) { type = clazz; // 默认的构造器 addDefaultConstructor(clazz); // get方法 addGetMethods(clazz); // set方法 addSetMethods(clazz); // 属性字段 addFields(clazz); // 从 getMethods 映射中获取可读属性名数组 readablePropertyNames = getMethods.keySet().toArray(new String[0]); // 从 setMethods 映射中获取可写属性名数组 writablePropertyNames = setMethods.keySet().toArray(new String[0]); // 将所有属性名的大写形式作为键,属性名作为值,存入到 caseInsensitivePropertyMap 中 for (String propName : readablePropertyNames) { caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName); } for (String propName : writablePropertyNames) { caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName); } } }
好我们看到有各种各样的集合存放着类的不同信息,那么我们来看看具体的每个收集的细节。
2.2 addDefaultConstructor默认构造器
private void addDefaultConstructor(Class<?> clazz) { // 获取到类的所有构造器 Constructor<?>[] constructors = clazz.getDeclaredConstructors(); // 判断有没有空参的构造器 有的话赋值给 defaultConstructor Arrays.stream(constructors).filter(constructor -> constructor.getParameterTypes().length == 0) .findAny().ifPresent(constructor -> this.defaultConstructor = constructor); }
2.3 addGetMethods获取getter
private void addGetMethods(Class<?> clazz) { // 存放属性有冲突的方法 Map<String, List<Method>> conflictingGetters = new HashMap<>(); // 获取类的所有方法自己的 继承父类的、接口的等 Method[] methods = getClassMethods(clazz); // 筛选出方法名是 get 或者 is 开头的方法 并且 方法的参数是 0 个的 添加进conflictingGetters的集合里 Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName())) .forEach(m -> addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m)); // 分析冲突 resolveGetterConflicts(conflictingGetters); } // 筛选出 get 或者 is 开头的 public static boolean isGetter(String name) { return (name.startsWith("get") && name.length() > 3) || (name.startsWith("is") && name.length() > 2); } /** * 获取方法的名字 * 比如setName 会得到 name * getName 得到 name * 也就是去掉前缀并首字母小写属性名 * @param name * @return */ public static String methodToProperty(String name) { if (name.startsWith("is")) { name = name.substring(2); } else if (name.startsWith("get") || name.startsWith("set")) { name = name.substring(3); } else { throw new ReflectionException("Error parsing property name '" + name + "'. Didn't start with 'is', 'get' or 'set'."); } if (name.length() == 1 || (name.length() > 1 && !Character.isUpperCase(name.charAt(1)))) { name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1); } return name; }
我们继续看getter的处理冲突的方法:
/** * 解决当属性有多个get或者is开头的方法时处理冲突 * @param conflictingGetters */ private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) { for (Entry<String, List<Method>> entry : conflictingGetters.entrySet()) { Method winner = null; String propName = entry.getKey(); boolean isAmbiguous = false; for (Method candidate : entry.getValue()) { if (winner == null) { winner = candidate; continue; } // 获取方法返回类型 Class<?> winnerType = winner.getReturnType(); Class<?> candidateType = candidate.getReturnType(); if (candidateType.equals(winnerType)) { /** * 如果返回值类型一致 * 不是boolean型的 则isAmbiguous = true * 是boolean型的 is开头的方法胜出 */ if (!boolean.class.equals(candidateType)) { isAmbiguous = true; break; } else if (candidate.getName().startsWith("is")) { winner = candidate; } } else if (candidateType.isAssignableFrom(winnerType)) { // 返回值类型不一致 并且当前类型是winnerType的爸爸 啥也不动 // OK getter type is descendant } else if (winnerType.isAssignableFrom(candidateType)) { // 返回值类型不一致 winnerType当前类型是的爸爸 那么candidate胜出 // 也就是要返回类型小的 要儿子型的 不要爸爸型的 winner = candidate; } else { // 都不成立 默认 isAmbiguous = true; isAmbiguous = true; break; } } /** * 开始添加 * 存在竞争的话 也会添加进 getMethods * 只是现在不会抛异常 当你去获取调用的时候就会抛异常了 * 也就是说异常后置了 */ addGetMethod(propName, winner, isAmbiguous); } } private void addGetMethod(String name, Method method, boolean isAmbiguous) { MethodInvoker invoker = isAmbiguous ? new AmbiguousMethodInvoker(method, MessageFormat.format( "Illegal overloaded getter method with ambiguous type for property ''{0}'' in class ''{1}''. This breaks the JavaBeans specification and can cause unpredictable results.", name, method.getDeclaringClass().getName())) : new MethodInvoker(method); // 添加进 getMethods key=name val=MethodInvoker getMethods.put(name, invoker); Type returnType = TypeParameterResolver.resolveReturnType(method, type); // 并将方法返回值也添加进 getTypes key=name val=返回值类型class getTypes.put(name, typeToClass(returnType)); }
getter总结一下的话就是:
- 筛选出get或者is开头的方法并且参数是空的;
- 没有冲突的话,挺好直接放进集合;
- 有冲突的话,返回值类型一致的话,并且是Boolean类型的话 is 开头的胜出;
- 有冲突的话,返回值类型一致的话,存在父子关系的话,类型小的胜出;
- 有冲突的话,返回值类型一致不存在父子关系或者返回值类型不一致那就是有冲突,并且会放一个异常包起来的方法,调用时报错异常后置。
2.4 addSetMethods获取setter
private void addSetMethods(Class<?> clazz) { // 一样存放冲突的 Map<String, List<Method>> conflictingSetters = new HashMap<>(); // 获取到所有的方法 Method[] methods = getClassMethods(clazz); // 筛选 set 开头的方法 并且参数只有一个的 放进conflictingSetters集合里 Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 1 && PropertyNamer.isSetter(m.getName())) .forEach(m -> addMethodConflict(conflictingSetters, PropertyNamer.methodToProperty(m.getName()), m)); // 处理冲突 resolveSetterConflicts(conflictingSetters); } private void addMethodConflict(Map<String, List<Method>> conflictingMethods, String name, Method method) { if (isValidPropertyName(name)) { List<Method> list = conflictingMethods.computeIfAbsent(name, k -> new ArrayList<>()); list.add(method); } }
再继续看看setter的冲突,大致跟getter差不多:
private void resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) { for (Entry<String, List<Method>> entry : conflictingSetters.entrySet()) { String propName = entry.getKey(); List<Method> setters = entry.getValue(); // 获取到属性的 get 方法返回的类型 Class<?> getterType = getTypes.get(propName); // 获取到 get方法 是不是有冲突的 boolean isGetterAmbiguous = getMethods.get(propName) instanceof AmbiguousMethodInvoker; boolean isSetterAmbiguous = false; Method match = null; for (Method setter : setters) { // 如果 对应的 get方法没有冲突 且 第一个参数的类型和 get方法的返回值类型一致 match=当前的set方法 直接不用判断别的有冲突的方法了 if (!isGetterAmbiguous && setter.getParameterTypes()[0].equals(getterType)) { // should be the best match match = setter; break; } if (!isSetterAmbiguous) { // 如果没有冲突 选择一个更好的setter match = pickBetterSetter(match, setter, propName); isSetterAmbiguous = match == null; } } if (match != null) { addSetMethod(propName, match); } } } private Method pickBetterSetter(Method setter1, Method setter2, String property) { // 如果还没有match到一个 也就是该属性的第一个方法进来的时候 直接返回setter2 if (setter1 == null) { return setter2; } // 获取到两个方法的第一个参数的类型 Class<?> paramType1 = setter1.getParameterTypes()[0]; Class<?> paramType2 = setter2.getParameterTypes()[0]; // paramType2的类型小的话 返回setter2 也就是还是选儿子 选小类型的 if (paramType1.isAssignableFrom(paramType2)) { return setter2; } else if (paramType2.isAssignableFrom(paramType1)) { return setter1; } // 两个参数类型不是父子关系的话 也是 setter是有冲突的 异常后置 MethodInvoker invoker = new AmbiguousMethodInvoker(setter1, MessageFormat.format( "Ambiguous setters defined for property ''{0}'' in class ''{1}'' with types ''{2}'' and ''{3}''.", property, setter2.getDeclaringClass().getName(), paramType1.getName(), paramType2.getName())); // 放进setMethods setMethods.put(property, invoker); Type[] paramTypes = TypeParameterResolver.resolveParamTypes(setter1, type); // 参数类型 放进 setTypes setTypes.put(property, typeToClass(paramTypes[0])); return null; }
private void addSetMethod(String name, Method method) { MethodInvoker invoker = new MethodInvoker(method); // 放进setMethods setMethods.put(name, invoker); Type[] paramTypes = TypeParameterResolver.resolveParamTypes(method, type); // 参数类型放进 setTypes setTypes.put(name, typeToClass(paramTypes[0])); }
setter大致总结的话:
- 筛选出set开头的和只有一个参数的所有方法;
- 先看get有没有冲突,get没有冲突的话,找到第一个参数和get方法的返回值类型一致的方法为最优;
- 没找到的话,如果只有一个set方法那就是没冲突直接就是它了;
- 有多个的话,判断当前和已经match的方法参数是否有父子关系,有的话选中类型小的;
- 有多个的话,发现其中方法参数有不是父子关系的那也是有冲突的,也是异常包起来,异常后置,调用的时候报错。
2.5 addFields补充属性的setter、getter
private void addFields(Class<?> clazz) { // 获得某个类的所有声明的字段,即包括public、private和protected,但是不包括父类的申明字段。 Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { if (!setMethods.containsKey(field.getName())) { /** * 属性没有set方法的话,且不是final和static的话 也可以放进setMethods 通过反射来设置值的 */ int modifiers = field.getModifiers(); if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) { addSetField(field); } } // 没有get方法的 给他加个get方法 也是通过反射来获取值的 if (!getMethods.containsKey(field.getName())) { addGetField(field); } } // 父类不为空的话 把父类的重新整一遍 递归调用 也就调一次最多 因为单继承 if (clazz.getSuperclass() != null) { addFields(clazz.getSuperclass()); } }
对于属性没有set方法的处理:
private void addSetField(Field field) { if (isValidPropertyName(field.getName())) { // 看设置的属性方法是SetFieldInvoker 纯纯的反射塞值 setMethods.put(field.getName(), new SetFieldInvoker(field)); Type fieldType = TypeParameterResolver.resolveFieldType(field, type); setTypes.put(field.getName(), typeToClass(fieldType)); } } public class SetFieldInvoker implements Invoker { private final Field field; public SetFieldInvoker(Field field) { this.field = field; } @Override public Object invoke(Object target, Object[] args) throws IllegalAccessException { try { field.set(target, args[0]); } catch (IllegalAccessException e) { if (Reflector.canControlMemberAccessible()) { field.setAccessible(true); field.set(target, args[0]); } else { throw e; } } return null; } @Override public Class<?> getType() { return field.getType(); } }
对于属性没有get方法的处理:
private void addGetField(Field field) { if (isValidPropertyName(field.getName())) { // 看设置的属性方法是 GetFieldInvoker 纯纯的反射拿值 getMethods.put(field.getName(), new GetFieldInvoker(field)); Type fieldType = TypeParameterResolver.resolveFieldType(field, type); getTypes.put(field.getName(), typeToClass(fieldType)); } } public class GetFieldInvoker implements Invoker { private final Field field; public GetFieldInvoker(Field field) { this.field = field; } @Override public Object invoke(Object target, Object[] args) throws IllegalAccessException { try { return field.get(target); } catch (IllegalAccessException e) { if (Reflector.canControlMemberAccessible()) { field.setAccessible(true); return field.get(target); } else { throw e; } } } @Override public Class<?> getType() { return field.getType(); } }
添加属性的话,主要是获取当前类有哪些自己的属性,有父类的话会递归调用,把父亲的属性也会塞进来的;
- 对于当发现属性没有setter,并且属性不是final和static修饰的就给他设置默认的setter。
- 对于属性没有getter,直接塞默认的getter
2.6 简单示例
3 小结
好了,Reflector的解析过程到这里就结束了,主要就是getter、setter、fields的处理和解析,有不对的地方欢迎指正哈。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了