MyBatis基础支持层位于 Mybatis 整体架构的最底层,支撑着 Mybatis 的核心处理层,是整个框架的基石。基础支持层中封装了多个较为通用的、独立的模块,不仅仅为 Mybatis 提供基础支撑,也可以在合适的场景中直接复用。
这篇介绍MyBatis的反射模块
反射工具箱 Mybatis 在进行参数处理、结果映射等操作时,会涉及大量的反射操作。Java 中的反射虽然功能强大,但是代码编写起来比较复杂且容易出错,为了简化反射操作的相关代码,Mybatis 提供了专门的反射模块,该模块位于 org.apache.ibatis.reflection
包中,它对常见的反射操作做了进一步封装,提供了更加简洁方便的反射 API。
Reflector & ReflectorFactory Reflector是MyBatis中反射模块的基础,每隔Reflector对象都对应一个类,在Reflector中缓存了反射操作需要使用的累的元信息。
Reflector中各个字段的含义如下:
Private Class <?> type; private String I readablepropertynames= EMPTY STRING ARRAYprivate String [writeablepropertynames EMPTY STRING ARRAY;private Map <String, Invoker> setmethods =new Hashmap <String, Invoker> ();private Map <String, Invoker> getmethods =new Hashmap <String, Invoker> ();private Map <String, Class <?>> settypes =new Hashmap <string, Class <?>> ();private Map <string, Class <?>> gettypes=new Hashmap <string, Class <?>> ();private Constructor <?> default Constructor; private Map <string, String> caseinsensitivepropertymap-new Hashmap <String, String> ();
在Reflector的构造方法中会解析制定的Class对象,并填充上述集合。具体如下:
public Reflector (Class<?> clazz) { type = clazz; addDefaultConstructor(clazz); addGetMethods(clazz); addSetMethods(clazz); addFields(clazz); readablePropertyNames = getMethods.keySet().toArray(new String [0 ]); writablePropertyNames = setMethods.keySet().toArray(new String [0 ]); for (String propName : readablePropertyNames) { caseInsensitivePropertyMap.put( propName.toUpperCase(Locale.ENGLISH), propName); } for (String propName : writablePropertyNames) { caseInsensitivePropertyMap.put( propName.toUpperCase(Locale.ENGLISH), propName); } }
Reflector.Addgetmethodso()
方法主要负责解析类中定义的 getter
方法,Reflector. addSetMethods()
方法负责解析类中定义的 setter
方法,两者的逻辑类似。
Reflector. Addgetmethods()
方法有如下三个核心步骤。
首先,调用Reflector.getClassMethods()方法获取当前类及其父类中定义的所有方法的唯一签名以及响应的Method对象。
private Method[] getClassMethods(Class<?> clazz) { Map<String, Method> uniqueMethods = new HashMap <>(); Class<?> currentClass = clazz; while (currentClass != null && currentClass != Object.class) { addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods()); Class<?>[] interfaces = currentClass.getInterfaces(); for (Class<?> anInterface : interfaces) { addUniqueMethods(uniqueMethods, anInterface.getMethods()); } currentClass = currentClass.getSuperclass(); } Collection<Method> methods = uniqueMethods.values(); return methods.toArray(new Method [0 ]); }
然后,按照JavaBean的规范,从Reflector.getClassMethods()方法返回的Method数组中查找该类中定义的getter方法,将其记录到conflictingGetters
集合中。conflictingGetters
集合的key为属性名称,value是该属性对应的getter方法的集合。
当子类覆盖了父类的getter方法且返回值发生变化时,在步骤1中就会产生两个签名不同的方法。
TypeParameterResolver 在开 始介绍 TypeParameterResolver之前,先简 单 介绍 一下Type接口的基础 知识 。Type是所有类 型的父接口,它 有四个 子接口和一个 实 现 类 ,如图所示。
下面来 看这 些子接口和子类 所代表的类 型。
Class 比较 常见 ,它 表示的是原始类 型。
Class类 的对 象表示JVM中的一个 类 或接口, 每个 Java类 在JVM里都表现 为 一个 Class对 象。
在程序中可以通过 “类名.class ”、“**对象.getClass()”或是“ Class.forName(“类 名”)**”等方式获 取Class对 象。
数 组 也被映射为 Class对 象,所有元素类 型相同且维 数 相同的数 组 都共享同一个 Class对 象。
ParameterizedType 表示的是参 数 化类 型,例如 List、 Map<Integer,String>、 Service这 种 带 有泛型的类 型。
ParameterizedType接口中常用的方法有三个 ,分别 是:
Type getRawType()
—–返回参 数 化类 型中的原始类 型,例如List的原始类 型为 List。
Type[] getActualTypeArguments()
—–获 取参 数 化类 型的类 型变 量或是实 际 类 型列 表,例如Map<Integer,String>的实 际 泛型列表Integer和 String。需要注意的是, 该 列表的元素类 型都是Type,也就是说 ,可能存在多层 嵌套的情况 。
Type getOwnerType()
—–返回是类 型所属 的类 型,例如存在A类 ,其中定义 了 内 部类 InnerA,则 InnerA属 的类 型为 A,如果是顶 层 类 型则 返回null。 这 种 关 系比较 常见 的示例是Map<K,V>接口与 Map.Entry<K,V>接口,Map<K,V> 接 口 是Map<K,V>接口的所有者。
TypeVariable
表示的是类 型变 量,它 用来 反映在JVM编 译 该 泛型前的信息。
例如List 中的T就是类 型变 量,它 在编 译 时 需被转 换 为 一个 具体 的类 型后才能正常使用。
该 接口中常用的方法有三个 ,分别 是:
Type[] getBounds()
—–获 取类 型变 量的上边 界,如果未明确 声 明上边 界则 默认 为 Object。
例如 class Test中 K 的上界就是 Person。
D getGenericDeclaration()
—–获 取声 明该 类 型变 量的原始类 型,例 如 class Test 中 的原始类 型是 Test。
String getName()
— 获 取在源码 中定义 时 的名字,上例中为 K。
GenericArrayType
表示的是数 组 类 型且组 成元素是 ParameterizedType 或 TypeVariable。
例如 List或T[]。该 接口只有 Type getGenericComponentType()—个 方法,它 返回数 组 的组 成元素。
WildcardType
表示的是通配符泛型,例如? extends Number
和? uper Integer
。
WildcardType接口有两 个 方法,分别 是:
Type[] getUpperBounds()
—-返回泛型变 量的上界。
Type[] getLowerBounds()
—- 返回泛型变 量的下界。
回到对 TypeParameterResolve
,它 是一个 工具类 ,提供了一系列静 态 方法来 解析指定类 中的字段、方法返回值 或方法参 数 的类 型。TypeParameterResolver中各个 静 态 方法之间 的调 用关 系大致如下图 所示,为 保持清 晰 ,其中递 归 调 用没 有表现 出来 ,在后面 的代码 分析过 程中会 进 行强调 。
TypeParameterResolver
中 通 过 resolveFieldType()
方 法 、 resolveRetumType()
方 法 、 resolveParamTypes()
方法分别 解析字段类 型、方法返回值 类 型和方法参 数 列表中各个 参 数 的类 型。 这 三个 方法的逻 辑 基本类 似,这 里以resolveFieldType()方法为 例进 行介绍 ,TypeParameterResolver.resolveFieldType()
方法的具体 实 现 如下:
public static Type resolveFieldType (Field field, Type srcType) { Type fieldType = field.getGenericType(); Class<?> declaringClass = field.getDeclaringClass(); return resolveType(fieldType, srcType, declaringClass); }
上述三个 方法都会 调 用resolveType()
方法,该 方法会 根据其第一个 参 数 的 类 型,即 字段、方法返回值 或方法参 数 的类 型,选 择 合适的方法进 行解析。resolveType()
方法的 第二个 参 数 表示查 找 该 字段、返回值 或方法参 数 的起始位置。第三个 参 数 则 表示该 字段、方法 定义 所在的类 。TypeParameterResolver.resolveType()
方法代码 如下:
private static Type resolveType (Type type, Type srcType, Class<?> declaringClass) { if (type instanceof TypeVariable) { return resolveTypeVar((TypeVariable<?>) type, srcType, declaringClass); } else if (type instanceof ParameterizedType) { return resolveParameterizedType((ParameterizedType) type, srcType, declaringClass); } else if (type instanceof GenericArrayType) { return resolveGenericArrayType((GenericArrayType) type, srcType, declaringClass); } else { return type; } }
为了便于理解,通过一个示例分析resolveType
方法, 假设有三个类 ClassA、SubClassA、TestType,代码如下:
ClassA
public class ClassA <K, V> { protected Map<K, V> map; }
SubClassA
public class SubClassA <T> extends ClassA <T,T> { }
TestType
public class TestType { SubClassA<Long> sa = new SubClassA (); public static void main (String[] args) throws NoSuchFieldException { Field f = ClassA.class.getDeclaredField("map" ); System.out.println(f.getGenericType()); System.out.println(f.getGenericType() instanceof ParameterizedType); Type type = TypeParameterResolver.resolveFieldType( f, ParameterizedTypelmpl.make(SubClassA.class, new Type []{Long.class}, TestType.class)); System.out.println(type.getClass()); ParameterizedType p = (ParameterizedType) type; System.out.println(p.getRawType()); System.out.println(p.getOwnerType()); for (Type t : p .getActualTypeArguments()) { System.out.println(t); } } }
根据前面的Type接口的介绍,上例中ClassA.map字段声明的类型Map<K,V>是ParamelerizedType类型,resolveType()方法回调用resolvParameterizedType()方法进行解析。
首先介绍resolveParameterizedType()方法的参数:
第一个参数是待解析的ParameterizedType类型
第二个参数是解析操作的起始类型
第三个参数为定义该字段或方法的类的Class对象
在该示例中第一个参数是Map<K,V>对应的ParameterizedType对象,第二个参数是TypeTest.SubA对应的ParameterizedType对象,第三个参数是ClassA(声明map字段的类)相应的Class对象。
继续分析scanSuperTypes()
方法 ,该方法回递归整个继承结构并完成类型变量的解析。在该示例之中,第一个参数K对应的TypeVariable对象,第二个参数是TypeText.SubA对应的ParameterizedType对象,第三个参数是ClassA(声明map字段的类)对应的Class对象,第四个参数是SubClassA对应的Class对象,第五个参数是Class<T,T>对应的ParameterizedType对象。
scanSuperTypes()
方法的具体实现如下:
private static Type scanSuperTypes ( TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass, Class<?> clazz, Type superclass) { Type result = null ; if (superclass instanceof ParameterizedType) { ParameterizedType parentAsType = (ParameterizedType) superclass; Class<?> parentAsClass = (Class<?>) parentAsType.getRawType(); if (declaringClass == parentAsClass) { Type[] typeArgs = parentAsType.getActualTypeArguments(); TypeVariable<?>[] declaredTypeVars = declaringClass.getTypeParameters(); for (int i = 0 ; i < declaredTypeVars.length; i++) { if (declaredTypeVars[i] == typeVar) { if (typeArgs[i] instanceof TypeVariable) { TypeVariable<?>[] typeParams = clazz.getTypeParameters(); for (int j = 0 ; j < typeParams.length; j++) { if (typeParams[j] == typeArgs[i]) { if (srcType instanceof ParameterizedType) { result = ((ParameterizedType) srcType).getActualTypeArguments()[j]; } break ; } } } else { result = typeArgs[i]; } } } } else if (declaringClass.isAssignableFrom(parentAsClass)) { result = resolveTypeVar(typeVar, parentAsType, declaringClass); } } else if (superclass instanceof Class) { if (declaringClass.isAssignableFrom((Class<?>) superclass)) { result = resolveTypeVar(typeVar, superclass, declaringClass); } } return result; }
下图展示了scanSuperTypes()方法解析类型变量的核心逻辑。
ObjectFactory MyBatis中很多模块会使用到ObjectFactory接口,该接口提供了多个create()方法的重载,通过这些create()方法可以创建指定类型的对象。ObjectFactory的定义如下:
public interface ObjectFactory { void setProperties (Properties properties) ; <T> T create (Class<T> type) ; <T> T create (Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) ; <T> boolean isCollection (Class<T> type) ; }
DefaultObjectFactory
是MyBatis
提供的ObjectFactory
接口的唯一实 现 ,它 是一个 反射工厂 , 其 create()
方法通过 调 用 instantiateClass()
方法实 现 。
DefaultObjectFactory.instantiateClass()
方法会 根据传 入的参 数 列表选 择 合适的构 造函数 实 例化对 象,具体 实 现 如下:
private <T> T instantiateClass (Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) { try { Constructor<T> constructor; if (constructorArgTypes == null || constructorArgs == null ) { constructor = type.getDeclaredConstructor(); if (!constructor.isAccessible()) { constructor.setAccessible(true ); } return constructor.newInstance(); } constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class [constructorArgTypes.size()])); if (!constructor.isAccessible()) { constructor.setAccessible(true ); } return constructor.newInstance(constructorArgs.toArray(new Object [constructorArgs.size()])); } catch (Exception e) { StringBuilder argTypes = new StringBuilder (); if (constructorArgTypes != null && !constructorArgTypes.isEmpty()) { for (Class<?> argType : constructorArgTypes) { argTypes.append(argType.getSimpleName()); argTypes.append("," ); } argTypes.deleteCharAt(argTypes.length() - 1 ); } StringBuilder argValues = new StringBuilder (); if (constructorArgs != null && !constructorArgs.isEmpty()) { for (Object argValue : constructorArgs) { argValues.append(String.valueOf(argValue)); argValues.append("," ); } argValues.deleteCharAt(argValues.length() - 1 ); } throw new ReflectionException ("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e); } }
除了使用MyBatis提供的DefaultObjectFactory
实现,还可以在mybatis-config.xml配置文件中指定自定义的ObjectFactory接口实现累, 从而实现功能上的扩展。
Property工具类 org.apache.ibatis.reflection.property
包下,提供了 PropertyCopier、PropertyNamer、PropertyTokenizer 三个属性相关的工具类。
PropertyTokenizer
在使用MyBatis的过程中,经常会碰到一些属性表达式,例如,在查询用户(User)的订单(Order)的结果集如下表所示:
user_name
order
item1
item2
…
Mary
124640
iPhone 8 plus
MacBook Pro
…
Lisa
46546
iPhone 11 Pro
Mac Pro
…
…
…
…
…
…
对象模型如下:
假设现在需要将结果集中的item1
列雨用户第一个订单(Order)的第一条目(Item)的名称映射,item2
与用户第一个订单的(Order)的第二条目(Item)的名称映射(这里仅仅是一个示例,在实际生产中很少这样的设计),我们可以得到下面的映射规则:
<resultMap id ="rm4testProTool" type ="User" > <id column ="id" property ="id" /> <result property ="orders[0].items[0].name" column ="iteml" /> <result property ="orders[0].items[1].name" column ="item2" /> </resultMap >
在上面的例子中,orders[0].items[0].name
这种由 .
和[]
组成的表达式是由PropertyTokenizer
进行解析的。以下是此类的源码:
public class PropertyTokenizer implements Iterator <PropertyTokenizer> { private String name; private final String indexedName; private String index; private final String children; public PropertyTokenizer (String fullname) { int delim = fullname.indexOf('.' ); if (delim > -1 ) { name = fullname.substring(0 , delim); children = fullname.substring(delim + 1 ); } else { name = fullname; children = null ; } indexedName = name; delim = name.indexOf('[' ); if (delim > -1 ) { index = name.substring(delim + 1 , name.length() - 1 ); 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
继承了Iterator
接口,它可以迭代处理嵌套多层表达式。
next()
方法中会创建PropertyTokenizer
对象并解析children
字段记录的子表达式。
继续使用订单示例进行说明,描述解析属性表达式orders[0].items[0].name
的迭代过程:
PropertyNamer
PropertyNamer
是另一个工具类,提供了下列静态方法帮助完成方法名到属性名的转换,以及多种检测操作。
public final class PropertyNamer { private PropertyNamer () { } 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; } public static boolean isProperty (String name) { return name.startsWith("get" ) || name.startsWith("set" ) || name.startsWith("is" ); } public static boolean isGetter (String name) { return name.startsWith("get" ) || name.startsWith("is" ); } public static boolean isSetter (String name) { return name.startsWith("set" ); } }
PropertyCopier
PropertyCopier
是一个属性拷贝的工具类,核心方法是copyBeanProperties()
方法,主要实现相同类型的两个对象之间的属性值拷贝,具体如下:
public final class PropertyCopier { private PropertyCopier () { } public static void copyBeanProperties (Class<?> type, Object sourceBean, Object destinationBean) { Class<?> parent = type; while (parent != null ) { final Field[] fields = parent.getDeclaredFields(); for (Field field : fields) { try { field.setAccessible(true ); field.set(destinationBean, field.get(sourceBean)); } catch (Exception e) { } } parent = parent.getSuperclass(); } } }
org.apache.ibatis.reflection.MetaClass
,类的元数据,基于 Reflector 和 PropertyTokenizer ,提供对指定类的各种骚操作。实现了对复杂的属性表达式的解析,并实现了获取指定属性描述信息的功能。
代码:
public class MetaClass { 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); } public MetaClass metaClassForProperty (String name) { Class<?> propType = reflector.getGetterType(name); return MetaClass.forClass(propType, reflectorFactory); } public String findProperty (String name) { StringBuilder prop = buildProperty(name, new StringBuilder ()); return prop.length() > 0 ? prop.toString() : null ; } public String findProperty (String name, boolean useCamelCaseMapping) { if (useCamelCaseMapping) { name = name.replace("_" , "" ); } return findProperty(name); } public String[] getGetterNames() { return reflector.getGetablePropertyNames(); } public String[] getSetterNames() { return reflector.getSetablePropertyNames(); } public Class<?> getSetterType(String name) { PropertyTokenizer prop = new PropertyTokenizer (name); if (prop.hasNext()) { MetaClass metaProp = metaClassForProperty(prop.getName()); return metaProp.getSetterType(prop.getChildren()); } else { return reflector.getSetterType(prop.getName()); } } public Class<?> getGetterType(String name) { PropertyTokenizer prop = new PropertyTokenizer (name); if (prop.hasNext()) { MetaClass metaProp = metaClassForProperty(prop); return metaProp.getGetterType(prop.getChildren()); } return getGetterType(prop); } private MetaClass metaClassForProperty (PropertyTokenizer prop) { Class<?> propType = getGetterType(prop); return MetaClass.forClass(propType, reflectorFactory); } private Class<?> getGetterType(PropertyTokenizer prop) { Class<?> type = reflector.getGetterType(prop.getName()); if (prop.getIndex() != null && Collection.class.isAssignableFrom(type)) { Type returnType = getGenericGetterType(prop.getName()); if (returnType instanceof ParameterizedType) { Type[] actualTypeArguments = ((ParameterizedType) returnType).getActualTypeArguments(); if (actualTypeArguments != null && actualTypeArguments.length == 1 ) { returnType = actualTypeArguments[0 ]; if (returnType instanceof Class) { type = (Class<?>) returnType; } else if (returnType instanceof ParameterizedType) { type = (Class<?>) ((ParameterizedType) returnType).getRawType(); } } } } return type; } private Type getGenericGetterType (String propertyName) { try { Invoker invoker = reflector.getGetInvoker(propertyName); if (invoker instanceof MethodInvoker) { Field _method = MethodInvoker.class.getDeclaredField("method" ); _method.setAccessible(true ); Method method = (Method) _method.get(invoker); return TypeParameterResolver.resolveReturnType(method, reflector.getType()); } else if (invoker instanceof GetFieldInvoker) { Field _field = GetFieldInvoker.class.getDeclaredField("field" ); _field.setAccessible(true ); Field field = (Field) _field.get(invoker); return TypeParameterResolver.resolveFieldType(field, reflector.getType()); } } catch (NoSuchFieldException e) { } catch (IllegalAccessException e) { } return null ; } public boolean hasSetter (String name) { PropertyTokenizer prop = new PropertyTokenizer (name); if (prop.hasNext()) { if (reflector.hasSetter(prop.getName())) { MetaClass metaProp = metaClassForProperty(prop.getName()); return metaProp.hasSetter(prop.getChildren()); } else { return false ; } } else { return reflector.hasSetter(prop.getName()); } } public boolean hasGetter (String name) { PropertyTokenizer prop = new PropertyTokenizer (name); if (prop.hasNext()) { if (reflector.hasGetter(prop.getName())) { MetaClass metaProp = metaClassForProperty(prop); return metaProp.hasGetter(prop.getChildren()); } else { return false ; } } else { return reflector.hasGetter(prop.getName()); } } public Invoker getGetInvoker (String name) { return reflector.getGetInvoker(name); } public Invoker getSetInvoker (String name) { return reflector.getSetInvoker(name); } private StringBuilder buildProperty (String name, StringBuilder builder) { PropertyTokenizer prop = new PropertyTokenizer (name); if (prop.hasNext()) { String propertyName = reflector.findPropertyName(prop.getName()); if (propertyName != null ) { builder.append(propertyName); builder.append("." ); MetaClass metaProp = metaClassForProperty(propertyName); metaProp.buildProperty(prop.getChildren(), builder); } } else { String propertyName = reflector.findPropertyName(name); if (propertyName != null ) { builder.append(propertyName); } } return builder; } public boolean hasDefaultConstructor () { return reflector.hasDefaultConstructor(); } }
MetaClass中比较重要的是findProperty(),它是通过调用MetaClass.buildProperty()方法实现的, 二buildProperty()方法会通过PropertyTokenizer解析复杂的属性表达式
ObjectWrapper MetaClass
是Mybatis对类级别的元信息的封装和处理,下面来看MyBatis对对象级别的元信息的处理。ObjectWrapper
接口是对对象的包装,抽象了对象的属性信息,它定义了一系列查询对象属性信息的方法,以及更新属性的方法。
public interface ObjectWrapper { Object get (PropertyTokenizer prop) ; void set (PropertyTokenizer prop, Object value) ; String findProperty (String name, boolean useCamelCaseMapping) ; String[] getGetterNames(); String[] getSetterNames(); Class<?> getSetterType(String name); Class<?> getGetterType(String name); boolean hasSetter (String name) ; boolean hasGetter (String name) ; MetaObject instantiatePropertyValue (String name, PropertyTokenizer prop, ObjectFactory objectFactory) ; boolean isCollection () ; void add (Object element) ; <E> void addAll (List<E> element) ; }
ObjectWrapperFactory负责创建ObjectWrapper对象。
DefaultObjectWrapperFactory
实 现 了 ObjectWrapperFactory
接口,但它 实 现 的 getWrapperFor()
方法始终 抛出异 常,hasWrapperFor()
方法始终 返回false,所以该 实 现 实 际 上是不可用的。但是 与 ObjectFactory 类 似,我们 可以在 mybatis-config.xml 中配置自定义 的 ObjectWrapperFactory 实 现 类 进 行扩 展,在后面介绍 MyBatis初始化时 还 会 提到该 扩 展点。
BaseWrapper
是一个实现了ObjectWrapper
接口的抽象类,其中封装了MetaObject
对象,并提供了三个方勇的方法提供其子类使用:
BaseWrapper,resolveCollection()方法会 调 用 MetaObject.get Value()方法,它 会 解析属 性表达 式并 获 取指定的属 性。
BaseWrapper.getCollectionValue()方法和 setCollectionValue()方法会 解析属 性表达 式的索引 信息,然后获 取/设 置对 应 项 。
org.apache.ibatis.reflection.MetaObject
,对象元数据,提供了对象的属性值的获得和设置等等方法。 可以理解成,对 BaseWrapper 操作的进一步增强 。
ObjectWrapper提供了获取/设置对象中指定的属性值、检测getter/setter等常用功能,但是ObjectWrapper只是这些功能的最后一站,我们省略了对属性表达式解析过程的介绍,而该解析过程是在MetaObject中实现的。
MetaObject 中字段的含义:
private final Object originalObject;private final ObjectWrapper objectWrapper;private final ObjectFactory objectFactory;private final ObjectWrapperFactory objectWrapperFactory;private final ReflectorFactory reflectorFactory;
MetaObject的构造方法 会根据传入的原始对象的类型以及ObjectFactory工厂的实现, 创建相应的ObjectWrapper对象:
private MetaObject ( Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) { this .originalObject = object; this .objectFactory = objectFactory; this .objectWrapperFactory = objectWrapperFactory; this .reflectorFactory = reflectorFactory; if (object instanceof ObjectWrapper) { this .objectWrapper = (ObjectWrapper) object; } else if (objectWrapperFactory.hasWrapperFor(object)) { this .objectWrapper = objectWrapperFactory.getWrapperFor(this , object); } else if (object instanceof Map) { this .objectWrapper = new MapWrapper (this , (Map) object); } else if (object instanceof Collection) { this .objectWrapper = new CollectionWrapper (this , (Collection) object); } else { this .objectWrapper = new BeanWrapper (this , object); } }
MetaObject和ObjectWrapper中关于类级别的方法,例如hasGetter()、hasSetter()、findProperty()等方法,都是直接调用MetaClass的对应方法实现的。
其他方法都是关于对象级别的方法,这些方法都是与ObjectWrapper配合实现,例如MetaObject.getValue()/setValue()方法。
以下是getValue()的代码:
public Object getValue (String name) { PropertyTokenizer prop = new PropertyTokenizer (name); if (prop.hasNext()) { MetaObject metaValue = metaObjectForProperty(prop.getIndexedName()); if (metaValue == SystemMetaObject.NULL_META_OBJECT) { return null ; } else { return metaValue.getValue(prop.getChildren()); } } else { return objectWrapper.get(prop); } }
类型转换 JDBC数据类型与Java语言中的数据类型并不是完全对应的,所以在PreparedStatement为SQL语句绑定参数时,需要从Java类型转换成JDBC类型,而从结果集获取数据的时候,则需要从JDBC类型转换成Java类型。
MyBatis使用类型转换处理器完车过了上述两种转换。
在 MyBatis中使用JdbcType这 个 枚举 类 型代表JDBC中的数 据类 型,该 枚举 类 型中定义 了 TYPE_CODE字段,记 录 了 JDBC类 型在java.sql.Types中相应 的常量 编 码 ,并 通过 一个 静 态 集合codeLookup (HashMap<Integer,JdbcType>类 型)维 护 了常量编 码 与JdbcType之间的对应关系。
TypeHandler MyBatis中所有的类 型转 换 器都继 承了 TypeHandler接口,在 TypeHandler接口中定义 了如 下四个 方法,这 四个 方法分为 两 类 :setParameter()方法负 责 将 数 据由JdbcType类 型转 换 成Java 类 型:getResult()
方法及其重载 负 责 将 数 据由Java类 型转 换 成JdbcType类 型。
public interface TypeHandler <T> { void setParameter (PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException; T getResult (ResultSet rs, String columnName) throws SQLException; T getResult (ResultSet rs, int columnIndex) throws SQLException; T getResult (CallableStatement cs, int columnIndex) throws SQLException; }
为了方便用户自定义TypeHandler实现,MyBatis提供了BaseTypeHandler这个抽象类,它实现了TypeHandler接口,并继承了TypeReference抽象类,其继承结构如下:
在BaseTypeHandler中实现了TypeHandler.setParameter()
方法和TypeHandler.getResult()
方法,具体实现如下。需要注意的是,这两个方法对于非空数据的处理都交给了子类实现。
public abstract class BaseTypeHandler <T> extends TypeReference <T> implements TypeHandler <T> { protected Configuration configuration; public void setConfiguration (Configuration c) { this .configuration = c; } @Override public void setParameter (PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException { if (parameter == null ) { if (jdbcType == null ) { throw new TypeException ("JDBC requires that the JdbcType must be specified for all nullable parameters." ); } try { ps.setNull(i, jdbcType.TYPE_CODE); } catch (SQLException e) { throw new TypeException ("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " + "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " + "Cause: " + e, e); } } else { try { setNonNullParameter(ps, i, parameter, jdbcType); } catch (Exception e) { throw new TypeException ("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . " + "Try setting a different JdbcType for this parameter or a different configuration property. " + "Cause: " + e, e); } } } @Override public T getResult (ResultSet rs, String columnName) throws SQLException { T result; try { result = getNullableResult(rs, columnName); } catch (Exception e) { throw new ResultMapException ("Error attempting to get column '" + columnName + "' from result set. Cause: " + e, e); } if (rs.wasNull()) { return null ; } else { return result; } } @Override public T getResult (ResultSet rs, int columnIndex) throws SQLException { T result; try { result = getNullableResult(rs, columnIndex); } catch (Exception e) { throw new ResultMapException ("Error attempting to get column #" + columnIndex+ " from result set. Cause: " + e, e); } if (rs.wasNull()) { return null ; } else { return result; } } @Override public T getResult (CallableStatement cs, int columnIndex) throws SQLException { T result; try { result = getNullableResult(cs, columnIndex); } catch (Exception e) { throw new ResultMapException ("Error attempting to get column #" + columnIndex+ " from callable statement. Cause: " + e, e); } if (cs.wasNull()) { return null ; } else { return result; } } public abstract void setNonNullParameter (PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException; public abstract T getNullableResult (ResultSet rs, String columnName) throws SQLException; public abstract T getNullableResult (ResultSet rs, int columnIndex) throws SQLException; public abstract T getNullableResult (CallableStatement cs, int columnIndex) throws SQLException; }
一般情况下, TypeHandler用于完成单个参数以及单个列值的类型转换, 如果存在多列值转换成一个Java对象的需求,应该优先考虑在使用映射文件中定义合适的映射规则()完成映射。
TypeHandlerRegistry 介绍完TypeHandler接口及其功能之后,MyBatis如何管理众多的TypeHanlder接口实现,如何知道何时使用哪个TypeHandler接口实现完成转换呢?
这个工作是由TypeHandlerRegistry完成的,在MyBatis初始化过程中,会为所有已知的TypeHanlder创建对象,并实现注册到TypeHandlerRegistry中, 由TypeHandlerRegistry负责管理这些TypeHandler对象。
TypeHandlerRegistry 中核心字段的含义:
private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap <JdbcType, TypeHandler<?>>(JdbcType.class); private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new ConcurrentHashMap <Type, Map<JdbcType, TypeHandler<?>>>(); private final TypeHandler<Object> UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler (this ); private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap <Class<?>, TypeHandler<?>>();
1. 注册TypeHandler对象
TypeHandlerRegistry.register()方法实 现 了注册 TypeHandler对 象的功能,该 注册 过 程会 向上 述四个 集合中添加TypeHandler对 象。register()方法有多个 重载 ,这 些重载 之间 的调 用关 系如图 。
从 上图中可以看出,多数 register()方法最终 会 调 用重载(4) 完成注册 功能,先来看该方法的实现,其三个 参 数 分别 指定了 TypeHandler能够 处 理的Java类 型、Jdbc类 型以及 TypeHandler对 象。
重载(4)
private void register (Type javaType, JdbcType jdbcType, TypeHandler<?> handler) { if (javaType != null ) { Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.get(javaType); if (map == null || map == NULL_TYPE_HANDLER_MAP) { map = new HashMap <JdbcType, TypeHandler<?>>(); TYPE_HANDLER_MAP.put(javaType, map); } map.put(jdbcType, handler); } ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler); }
在(1)〜(3)这 三个 register()
方法重载 中会 尝 试 读 取TypeHandler
类 中定义 的@MappedTypes
注解和@MappedJdbcTypes
注解,@MappedTypes
注解用于指明该 TypeHandler
实 现 类 能够 处 理 的Java类 型的集合,@MappedJdbcTypes
注解用于指明该 TypeHandler
实 现 类 能够 处 理的JDBC 类 型集合。register()
方法的重载(1)〜(3)的具体 实 现 如下:
重载(1)
public void register (Class<?> typeHandlerClass) { boolean mappedTypeFound = false ; MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class); if (mappedTypes != null ) { for (Class<?> javaTypeClass : mappedTypes.value()) { register(javaTypeClass, typeHandlerClass); mappedTypeFound = true ; } } if (!mappedTypeFound) { register(getInstance(null , typeHandlerClass)); } }
重载(2)
public <T> void register (TypeHandler<T> typeHandler) { boolean mappedTypeFound = false ; MappedTypes mappedTypes = typeHandler.getClass().getAnnotation(MappedTypes.class); if (mappedTypes != null ) { for (Class<?> handledType : mappedTypes.value()) { register(handledType, typeHandler); mappedTypeFound = true ; } } if (!mappedTypeFound && typeHandler instanceof TypeReference) { try { TypeReference<T> typeReference = (TypeReference<T>) typeHandler; register(typeReference.getRawType(), typeHandler); mappedTypeFound = true ; } catch (Throwable t) { } } if (!mappedTypeFound) { register((Class<T>) null , typeHandler); } }
重载(3)
private <T> void register (Type javaType, TypeHandler<? extends T> typeHandler) { MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class); if (mappedJdbcTypes != null ) { for (JdbcType handledJdbcType : mappedJdbcTypes.value()) { register(javaType, handledJdbcType, typeHandler); } if (mappedJdbcTypes.includeNullJdbcType()) { register(javaType, null , typeHandler); } } else { register(javaType, null , typeHandler); } }
上 述 全 部 的 register()
方 法 重 载 都 是 在 向 TYPE_HANDLER_MAP
集 合 和 ALL_TYPE_HANDLERS_MAP
集合注册 TypeHandler
对 象,而重载(5)是向 JDBC_TYPE_HANDLER_MAP
集合注册 TypeHandler
对 象,其具体 实 现 如下:
重载(5)
public void register (JdbcType jdbcType, TypeHandler<?> handler) { JDBC_TYPE_HANDLER_MAP.put(jdbcType, handler); }
TypeHandlerRegistry
除了提供注册 单 个 TypeHandler的register()
重载 ,还 可以扫 描整个 包下 的TypeHandler
接口实 现 类 ,并 将 完成这 些TypeHandler
实 现 类 的注册 。
重载(6)
public void register (String packageName) { ResolverUtil<Class<?>> resolverUtil = new ResolverUtil <Class<?>>(); resolverUtil.find(new ResolverUtil .IsA(TypeHandler.class), packageName); Set<Class<? extends Class <?>>> handlerSet = resolverUtil.getClasses(); for (Class<?> type : handlerSet) { if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) { register(type); } } }
2. 查找TypeHandler
TypeHandlerRegistry
提供了查 找 TypeHandler
对 象的功能。TypeHandlerRegistry.getTypeHandler()
方法实 现 了从 上述四个 集合中获 取对 应 TypeHandler
对 象的功能。TypeHandlerRegistry.getTypeHandler()方法有多个 重载 ,这 些重 载 之间 的关 系如下图所示。
经 过 一系列类 型转 换 之后,TypeHandlerRegistry.getTypeHandler()
方法的多个 重载 都会 调 用 TypeHandlerRegistry.getTypeHandle(Type, JdbcType)
这 个 重载 方法,它 会 根据指定的 Java 类 型和 JdbcType类 型查 找 相应 的TypeHandler对 象。
TypeAliasRegistry 在编 写 SQL语 句时 ,使用别 名可以方便理解以及维 护 ,例如表名或列名很 长 时 ,我们 一般 会 为 其设 计 易懂 易维 护 的别 名。MyBatis将 SQL语 句中别 名的概 念进 行了延伸和扩 展,MyBatis 可以为 一个 类 添加一个 别 名,之后就可以通过 别 名引用该 类 。
MyBatis
通过 TypeAliasRegistry
类 完成别 名注册 和管理的功能,TypeAliasRegistry
的结 构 比 较 简 单 ,它 通过 TYPE_ALIASES
字段(Map<String,Class<?>>
类 型)管理别 名与 Java类 型之间 的对 应 关 系,通过 TypeAHasRegistiy.registerAlias()
方法完成注册 别 名,该 方法的具体 实 现 如下:
public void registerAlias (String alias, Class<?> value) { if (alias == null ) { throw new TypeException ("The parameter alias cannot be null" ); } String key = alias.toLowerCase(Locale.ENGLISH); if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) { throw new TypeException ("..." ); } TYPE_ALIASES.put(key, value); }
在 TypeAliasRegistry
的构 造方法中,默认 为 Java的基本类 型及其数 组 类 型、基本类 型的封 装 类 及其数组类型、Date、BigDecimal、Biglnteger、Map、HashMap、List、ArrayList、Collection、 Iterator、ResultSet等类 型添加了别名。
参考
《MyBatis技术内幕》
部分图片来源——《MyBatis技术内幕》