在之前的配置文件解析过程中,我们有看到metaClass这个类,这里我们花一章来分析metaClass的源码来进而分析这个类的作用。
二、源码分析 2.1metaClass类源码分析元信息类metaClass的构造方法为私有类型,所以不能直接创建,必须使用其提供的forClass方法进行创建。它的创建逻辑如下:
public class metaClass { //Reflector 的工厂类,兼有缓存 Reflector 对象的功能 private final ReflectorFactory reflectorFactory; // 反射器,用于解析和存储目标类中的元信息 private final Reflector reflector; private metaClass(Class> type, ReflectorFactory reflectorFactory) { this.reflectorFactory = reflectorFactory; this.reflector = reflectorFactory.findForClass(type); } public static metaClass forClass(Class> type, ReflectorFactory reflectorFactory) { return new metaClass(type, reflectorFactory); } // ......省略其他代码 }
上面出现了两个新的类ReflectorFactory和Reflector,这个两个新的类我们先放一放,后面再具体看这个两个类的作用,我们在配置文件解析 settings有看过这样子的调用,如下:
private Properties settingsAsProperties(XNode context) { if (context == null) { return new Properties(); } Properties props = context.getChildrenAsProperties(); // Check that all settings are known to the configuration class metaClass metaConfig = metaClass.forClass(Configuration.class, localReflectorFactory); for (Object key : props.keySet()) { if (!metaConfig.hasSetter(String.valueOf(key))) { throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive)."); } } return props; }
我们接下来看看metaClass的hasSetter方法的源码。
public boolean hasSetter(String name) { // 属性分词器,用于解析属性名 PropertyTokenizer prop = new PropertyTokenizer(name); // hasNext 返回 true,则表明 name 是一个复合属性,后面会进行分析 if (prop.hasNext()) { // 调用 reflector 的 hasSetter 方法 if (reflector.hasSetter(prop.getName())) { // 为属性创建创建 metaClass metaClass metaProp = metaClassForProperty(prop.getName()); // 再次调用 hasSetter return metaProp.hasSetter(prop.getChildren()); } else { return false; } } else { // 调用 reflector 的 hasSetter 方法 return reflector.hasSetter(prop.getName()); } }
从上面的代码中,我们可以看出 metaClass 中的 hasSetter 方法最终调用了 Reflector 的 hasSetter 方法。关于 Reflector 的 hasSetter 方法。
下面单独分析一下这几个类的逻辑,首先是ReflectorFactory。ReflectorFactory 是一个接口,MyBatis 中目前只有一个实现类DefaultReflectorFactory,它的分析如下:
2.2DefaultReflectorFactory源码分析DefaultReflectorFactory 用于创建 Reflector,同时兼有缓存的功能,它的源码如下。
public class DefaultReflectorFactory implements ReflectorFactory { private boolean classCacheEnabled = true; private final ConcurrentMap, Reflector> reflectorMap = new ConcurrentHashMap , Reflector>(); public DefaultReflectorFactory() { } @Override public boolean isClassCacheEnabled() { return classCacheEnabled; } @Override public void setClassCacheEnabled(boolean classCacheEnabled) { this.classCacheEnabled = classCacheEnabled; } @Override public Reflector findForClass(Class> type) { // classCacheEnabled 默认为 true if (classCacheEnabled) { // 从缓存中获取 Reflector 对象 Reflector cached = reflectorMap.get(type); // 缓存为空,则创建一个新的 Reflector 实例,并放入缓存中 if (cached == null) { cached = new Reflector(type); // 将 映射缓存到 map 中,方便下次取用 reflectorMap.put(type, cached); } return cached; } else { // 创建一个新的 Reflector 实例 return new Reflector(type); } } }
DefaultReflectorFactory 的findForClass方法逻辑不是很复杂,包含两个访存 *** 作,和一个对象创建 *** 作。代码注释的比较清楚了,就不多说了。接下来,来分析一下反射器 Reflector。
2.3Reflector源码分析我们来看一下 Reflector 的源码。Reflector 这个类的用途主要是是通过反射获取目标类的 getter 方法及其返回值类型,setter 方法及其参数值类型等元信息。并将获取到的元信息缓存到相应的集合中,供后续使用。Reflector 本身代码比较多,这里不能一一分析。本小节,我将会分析三部分逻辑,分别如下:
- Reflector 构造方法及成员变量分析
- getter 方法解析过程
- setter 方法解析过程
接下来我们将进行分析:
2.3.1 Reflector 构造方法及成员变量分析Reflector 构造方法中包含了很多初始化逻辑,目标类的元信息解析过程也是在构造方法中完成的,这些元信息最终会被保存到 Reflector 的成员变量中。下面我们先来看看 Reflector 的构造方法和相关的成员变量定义,代码如下:
public class Reflector { private final Class> type; private final String[] readablePropertyNames; private final String[] writeablePropertyNames; private final MapsetMethods = new HashMap (); private final Map getMethods = new HashMap (); private final Map > setTypes = new HashMap >(); private final Map > getTypes = new HashMap >(); // 存储目标类的默认构造方法 private Constructor> defaultConstructor; // 用于保存大写属性名与属性名之间的映射,比如 private Map caseInsensitivePropertyMap = new HashMap (); public Reflector(Class> clazz) { type = clazz; // 解析目标类的默认构造方法,并赋值给 defaultConstructor 变量 addDefaultConstructor(clazz); // 解析 getter 方法,并将解析结果放入 getMethods 中 addGetMethods(clazz); // 解析 setter 方法,并将解析结果放入 setMethods 中 addSetMethods(clazz); // 解析属性字段,并将解析结果添加到 setMethods 或 getMethods 中 addFields(clazz); // 从 getMethods 映射中获取可读属性名数组 readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]); // 从 setMethods 映射中获取可写属性名数组 writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]); // 将所有属性名的大写形式作为键,属性名作为值,存入到 caseInsensitivePropertyMap 中 for (String propName : readablePropertyNames) { caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName); } for (String propName : writeablePropertyNames) { caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName); } } // ...省略部分代码 }
如上,Reflector 的构造方法看起来略为复杂,不过好在一些比较复杂的逻辑都封装在了相应的方法中,这样整体的逻辑就比较清晰了。Reflector 构造方法所做的事情均已进行了注释,大家对照着注释先看一下。相关方法的细节待会会进行分析。看完构造方法,下面我来通过表格的形式,列举一下 Reflector 部分成员变量的用途。如下:
getter方法的逻辑都封装在了addGetMethods中,接下来我们看下这个方法的源码信息:
private void addGetMethods(Class> cls) { Map> conflictingGetters = new HashMap >(); // 获取当前类,接口,以及父类中的方法。该方法逻辑不是很复杂,这里就不展开了 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); addMethodConflict(conflictingGetters, name, method); } } // 解决 getter 冲突 resolveGetterConflicts(conflictingGetters); }
它的执行流程如下:
- 获取当前类,接口,以及父类中的方法;
- 遍历上一步获取的方法数组,并过滤出以get和is开头的方法;
- 将方法名转换成相应的属性名;
- 将属性名和方法对象添加到冲突集合中;
- 解决冲突
前面几步的代码还是比较简单的,我们就来看下他是如何解决冲突的,源代码如下:
private void resolveGetterConflicts(Map> conflictingGetters) { for (Entry > 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(); 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."); } else if (candidate.getName().startsWith("is")) { winner = candidate; } } else if (candidateType.isAssignableFrom(winnerType)) { // OK getter type is descendant } 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."); } } addGetMethod(propName, winner); } }
其实他解决冲突方法的规则如下:
- 冲突方法的返回值类型具有继承关系,子类返回值对应的方法被认为是更合适的选择;
- 冲突方法的返回值类型相同,如果返回值类型为boolean,那么以is开头的方法则是更合适的方法;
- 冲突方法的返回值类型相同,但返回值类型非boolean,此时出现歧义,抛出异常;
- 冲突方法的返回值类型不相关,无法确定哪个是更好的选择,此时直接抛异常;
与 getter 方法解析过程相比,setter 方法的解析过程与此有一定的区别。主要体现在冲突出现的原因,以及冲突的解决方法上。那下面,我们深入源码来找出两者之间的区别;
private void addSetMethods(Class> cls) { Map> conflictingSetters = new HashMap >(); // 获取当前类,接口,以及父类中的方法。该方法逻辑不是很复杂,这里就不展开了 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); addMethodConflict(conflictingSetters, name, method); } } } // 解决 setter 冲突 resolveSetterConflicts(conflictingSetters); }
解决 setter 冲突resolveSetterConflicts源码分析如下:
private void resolveSetterConflicts(Map2.4PropertyTokenizer属性分词器分析> conflictingSetters) { for (String propName : conflictingSetters.keySet()) { List setters = conflictingSetters.get(propName); 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) { // there could still be the 'best match' match = null; exception = e; } } } // 若 match 为空,表示没找到更为合适的方法,此时抛出异常 if (match == null) { throw exception; } else { // 将筛选出的方法放入 setMethods 中,并将方法参数值添加到 setTypes 中 addSetMethod(propName, match); } } } 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() + "'."); }
对于较为复杂的属性,需要进行进一步解析才能使用。那什么样的属性是复杂属性呢?来看个测试代码就知道了。
public class metaClassTest { private class Author { private Integer id; private String name; private Integer age; private Article[] articles; // 省略 getter/setter } private class Article { private Integer id; private String title; private String content; private Author author; // 省略 getter/setter } public void testHasSetter() { // 为 Author 创建元信息对象 metaClass authormeta = metaClass.forClass(Author.class, new DefaultReflectorFactory()); System.out.println("------------☆ Author ☆------------"); System.out.println("id -> " + authormeta.hasSetter("id")); System.out.println("name -> " + authormeta.hasSetter("name")); System.out.println("age -> " + authormeta.hasSetter("age")); // 检测 Author 中是否包含 Article[] 的 setter System.out.println("articles -> " + authormeta.hasSetter("articles")); System.out.println("articles[] -> " + authormeta.hasSetter("articles[]")); System.out.println("title -> " + authormeta.hasSetter("title")); // 为 Article 创建元信息对象 metaClass articlemeta = metaClass.forClass(Article.class, new DefaultReflectorFactory()); System.out.println("n------------☆ Article ☆------------"); System.out.println("id -> " + articlemeta.hasSetter("id")); System.out.println("title -> " + articlemeta.hasSetter("title")); System.out.println("content -> " + articlemeta.hasSetter("content")); // 下面两个均为复杂属性,分别检测 Article 类中的 Author 类是否包含 id 和 name 的 setter 方法 System.out.println("author.id -> " + articlemeta.hasSetter("author.id")); System.out.println("author.name -> " + articlemeta.hasSetter("author.name")); } }
接下来我们对PropertyTokenizer的源码进行分析:
public PropertyTokenizer(String fullname) { // 检测传入的参数中是否包含字符 '.' int delim = fullname.indexOf('.'); if (delim > -1) { name = fullname.substring(0, delim); children = fullname.substring(delim + 1); } else { // fullname 中不存在字符 '.' name = fullname; children = null; } indexedName = name; // 检测传入的参数中是否包含字符 '[' delim = name.indexOf('['); if (delim > -1) { index = name.substring(delim + 1, name.length() - 1); // 获取分解符前面的内容,比如 fullname = articles[1],name = articles name = name.substring(0, delim); } } public String getName() { return name; } public String getIndex() { return index; } public String getIndexedName() { return indexedName; } public String getChildren() { return children; } @Override public boolean hasNext() { return children != null; } @Override public PropertyTokenizer next() { return new PropertyTokenizer(children); } @Override public void remove() { throw new UnsupportedOperationException("Remove is not supported, as it has no meaning in the context of properties."); } }
以上是 PropertyTokenizer 的源码分析,注释的比较多,应该分析清楚了。大家如果看懂了上面的分析,那么可以自行举例进行测试,以加深理解。
本节为了分析 metaClass 的 hasSetter 方法,把这个方法涉及到的源码均分析了一遍。其实,如果想简单点分析,我可以直接把 metaClass 当成一个黑盒,然后用一句话告诉大家 hasSetter 方法有什么用即可
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)