MyBatis 在進(jìn)行參數(shù)處理、結(jié)果映射時等操作時,會涉及大量的反射操作。為了簡化這些反射相關(guān)操作,MyBatis 在 org.apache.ibatis.reflection 包下提供了專門的反射模塊,對反射操作做了近一步封裝,提供了更為簡潔的 API。
Reflector
MyBatis 提供 Reflector 類來緩存類的字段名和 getter/setter 方法的元信息,使得反射時有更好的性能。使用方式是將原始類對象傳入其構(gòu)造方法,生成 Reflector 對象。
public Reflector(Class<?> clazz) {
type = clazz;
// 如果存在,記錄無參構(gòu)造方法
addDefaultConstructor(clazz);
// 記錄字段名與get方法、get方法返回值的映射關(guān)系
addGetMethods(clazz);
// 記錄字段名與set方法、set方法參數(shù)的映射關(guān)系
addSetMethods(clazz);
// 針對沒有g(shù)etter/setter方法的字段,通過Filed對象的反射來設(shè)置和讀取字段值
addFields(clazz);
// 可讀的字段名
readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
// 可寫的字段名
writablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
// 保存一份所有字段名大寫與原始字段名的隱射
for (String propName : readablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
for (String propName : writablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
}
addGetMethods 和 addSetMethods 分別獲取類的所有方法,從符合 getter/setter 規(guī)范的方法中解析出字段名,并記錄方法的參數(shù)類型、返回值類型等信息:
private void addGetMethods(Class<?> cls) {
// 字段名-get方法
Map<String, List<Method>> conflictingGetters = new HashMap<>();
// 獲取類的所有方法,及其實現(xiàn)接口的方法,并根據(jù)方法簽名去重
Method[] methods = getClassMethods(cls);
for (Method method : methods) {
if (method.getParameterTypes().length > 0) {
// 過濾有參方法
continue;
}
String name = method.getName();
if ((name.startsWith("get") && name.length() > 3)
|| (name.startsWith("is") && name.length() > 2)) {
// 由get屬性獲取對應(yīng)的字段名(去除前綴,首字母轉(zhuǎn)小寫)
name = PropertyNamer.methodToProperty(name);
addMethodConflict(conflictingGetters, name, method);
}
}
// 保證每個字段只對應(yīng)一個get方法
resolveGetterConflicts(conflictingGetters);
}
對 getter/setter 方法進(jìn)行去重是通過類似 java.lang.String#getSignature:java.lang.reflect.Method 的方法簽名來實現(xiàn)的,如果子類在實現(xiàn)過程中,參數(shù)、返回值使用了不同的類型(使用原類型的子類),則會導(dǎo)致方法簽名不一致,同一字段就會對應(yīng)不同的 getter/setter 方法,因此需要進(jìn)行去重。
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;
}
// 字段對應(yīng)了多個get方法
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")) {
// 返回值為boolean的get方法可能有多個,如getIsSave和isSave,優(yōu)先取is開頭的
winner = candidate;
}
} else if (candidateType.isAssignableFrom(winnerType)) {
// OK getter type is descendant
// 可能會出現(xiàn)接口中的方法返回值是List,子類實現(xiàn)方法返回值是ArrayList,使用子類返回值方法
} 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.");
}
}
// 記錄字段名對應(yīng)的get方法對象和返回值類型
addGetMethod(propName, winner);
}
}
去重的方式是使用更規(guī)范的方法以及使用子類的方法。在確認(rèn)字段名對應(yīng)的唯一 getter/setter 方法后,記錄方法名對應(yīng)的方法、參數(shù)、返回值等信息。MethodInvoker 可用于調(diào)用 Method 類的 invoke 方法來執(zhí)行 getter/setter 方法(addSetMethods 記錄映射關(guān)系的方式與 addGetMethods 大致相同)。
private void addGetMethod(String name, Method method) {
// 過濾$開頭、serialVersionUID的get方法和getClass()方法
if (isValidPropertyName(name)) {
// 字段名-對應(yīng)get方法的MethodInvoker對象
getMethods.put(name, new MethodInvoker(method));
Type returnType = TypeParameterResolver.resolveReturnType(method, type);
// 字段名-運(yùn)行時方法的真正返回類型
getTypes.put(name, typeToClass(returnType));
}
}
接下來會執(zhí)行 addFields 方法,此方法針對沒有 getter/setter 方法的字段,通過包裝為 SetFieldInvoker 在需要時通過 Field 對象的反射來設(shè)置和讀取字段值。
private void addFields(Class<?> clazz) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (!setMethods.containsKey(field.getName())) {
// issue #379 - removed the check for final because JDK 1.5 allows
// modification of final fields through reflection (JSR-133). (JGB)
// pr #16 - final static can only be set by the classloader
int modifiers = field.getModifiers();
if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
// 非final的static變量,沒有set方法,可以通過File對象做賦值操作
addSetField(field);
}
}
if (!getMethods.containsKey(field.getName())) {
addGetField(field);
}
}
if (clazz.getSuperclass() != null) {
// 遞歸查找父類
addFields(clazz.getSuperclass());
}
}
Invoker
Invoker 接口用于抽象設(shè)置和讀取字段值的操作。對于有 getter/setter 方法的字段,通過 MethodInvoker 反射執(zhí)行;對應(yīng)其它字段,通過 GetFieldInvoker 和 SetFieldInvoker 操作 Field 對象的 getter/setter 方法反射執(zhí)行。
/**
* 用于抽象設(shè)置和讀取字段值的操作
*
* {@link MethodInvoker} 反射執(zhí)行g(shù)etter/setter方法
* {@link GetFieldInvoker} {@link SetFieldInvoker} 反射執(zhí)行Field對象的get/set方法
*
* @author Clinton Begin
*/
public interface Invoker {
/**
* 通過反射設(shè)置或讀取字段值
*
* @param target
* @param args
* @return
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException;
/**
* 字段類型
*
* @return
*/
Class<?> getType();
}
TypeParameterResolver
針對 Java-Type 體系的多種實現(xiàn),TypeParameterResolver 提供一系列方法來解析指定類中的字段、方法返回值或方法參數(shù)的類型。
Type 接口包含 4 個子接口和 1 個實現(xiàn)類:

-
Class:原始類型 -
ParameterizedType:泛型類型,如:List<String> -
TypeVariable:泛型類型變量,如:List<T>中的T -
GenericArrayType:組成元素是ParameterizedType或TypeVariable的數(shù)組類型,如:List<String>[]、T[] -
WildcardType:通配符泛型類型變量,如:List<?>中的?
TypeParameterResolver 分別提供 resolveFieldType、resolveReturnType、resolveParamTypes 方法用于解析字段類型、方法返回值類型和方法入?yún)㈩愋?,這些方法均調(diào)用 resolveType 來獲取類型信息:
/**
* 獲取類型信息
*
* @param type 根據(jù)是否有泛型信息簽名選擇傳入泛型類型或簡單類型
* @param srcType 引用字段/方法的類(可能是子類,字段和方法在父類聲明)
* @param declaringClass 字段/方法聲明的類
* @return
*/
private static Type resolveType(Type type, Type srcType, Class<?> declaringClass) {
if (type instanceof TypeVariable) {
// 泛型類型變量,如:List<T> 中的 T
return resolveTypeVar((TypeVariable<?>) type, srcType, declaringClass);
} else if (type instanceof ParameterizedType) {
// 泛型類型,如:List<String>
return resolveParameterizedType((ParameterizedType) type, srcType, declaringClass);
} else if (type instanceof GenericArrayType) {
// TypeVariable/ParameterizedType 數(shù)組類型
return resolveGenericArrayType((GenericArrayType) type, srcType, declaringClass);
} else {
// 原始類型,直接返回
return type;
}
}
resolveTypeVar 用于解析泛型類型變量參數(shù)類型,如果字段或方法在當(dāng)前類中聲明,則返回泛型類型的上界或 Object 類型;如果在父類中聲明,則遞歸解析父類;父類也無法解析,則遞歸解析實現(xiàn)的接口。
private static Type resolveTypeVar(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass) {
Type result;
Class<?> clazz;
if (srcType instanceof Class) {
// 原始類型
clazz = (Class<?>) srcType;
} else if (srcType instanceof ParameterizedType) {
// 泛型類型,如 TestObj<String>
ParameterizedType parameterizedType = (ParameterizedType) srcType;
// 取原始類型TestObj
clazz = (Class<?>) parameterizedType.getRawType();
} else {
throw new IllegalArgumentException("The 2nd arg must be Class or ParameterizedType, but was: " + srcType.getClass());
}
if (clazz == declaringClass) {
// 字段就是在當(dāng)前引用類中聲明的
Type[] bounds = typeVar.getBounds();
if (bounds.length > 0) {
// 返回泛型類型變量上界,如:T extends String,則返回String
return bounds[0];
}
// 沒有上界返回Object
return Object.class;
}
// 字段/方法在父類中聲明,遞歸查找父類泛型
Type superclass = clazz.getGenericSuperclass();
result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superclass);
if (result != null) {
return result;
}
// 遞歸泛型接口
Type[] superInterfaces = clazz.getGenericInterfaces();
for (Type superInterface : superInterfaces) {
result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superInterface);
if (result != null) {
return result;
}
}
return Object.class;
}
通過調(diào)用 scanSuperTypes 實現(xiàn)遞歸解析:
private static Type scanSuperTypes(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass, Class<?> clazz, Type superclass) {
if (superclass instanceof ParameterizedType) {
// 父類是泛型類型
ParameterizedType parentAsType = (ParameterizedType) superclass;
Class<?> parentAsClass = (Class<?>) parentAsType.getRawType();
// 父類中的泛型類型變量集合
TypeVariable<?>[] parentTypeVars = parentAsClass.getTypeParameters();
if (srcType instanceof ParameterizedType) {
// 子類可能對父類泛型變量做過替換,使用替換后的類型
parentAsType = translateParentTypeVars((ParameterizedType) srcType, clazz, parentAsType);
}
if (declaringClass == parentAsClass) {
// 字段/方法在當(dāng)前父類中聲明
for (int i = 0; i < parentTypeVars.length; i++) {
if (typeVar == parentTypeVars[i]) {
// 使用變量對應(yīng)位置的真正類型(可能已經(jīng)被替換),如父類 A<T>,子類 B extends A<String>,則返回String
return parentAsType.getActualTypeArguments()[i];
}
}
}
// 字段/方法聲明的類是當(dāng)前父類的父類,繼續(xù)遞歸
if (declaringClass.isAssignableFrom(parentAsClass)) {
return resolveTypeVar(typeVar, parentAsType, declaringClass);
}
} else if (superclass instanceof Class && declaringClass.isAssignableFrom((Class<?>) superclass)) {
// 父類是原始類型,繼續(xù)遞歸父類
return resolveTypeVar(typeVar, superclass, declaringClass);
}
return null;
}
解析方法返回值和方法參數(shù)的邏輯大致與解析字段類型相同,MyBatis 源碼的TypeParameterResolverTest 類提供了相關(guān)的測試用例。
ReflectorFactory
MyBatis 還提供 ReflectorFactory 接口用于實現(xiàn) Reflector 容器,其默認(rèn)實現(xiàn)為 DefaultReflectorFactory,其中可以使用 classCacheEnabled 屬性來配置是否使用緩存。
public class DefaultReflectorFactory implements ReflectorFactory {
/**
* 是否緩存Reflector類信息
*/
private boolean classCacheEnabled = true;
/**
* Reflector緩存容器
*/
private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<>();
public DefaultReflectorFactory() {
}
@Override
public boolean isClassCacheEnabled() {
return classCacheEnabled;
}
@Override
public void setClassCacheEnabled(boolean classCacheEnabled) {
this.classCacheEnabled = classCacheEnabled;
}
/**
* 獲取類的Reflector信息
*
* @param type
* @return
*/
@Override
public Reflector findForClass(Class<?> type) {
if (classCacheEnabled) {
// synchronized (type) removed see issue #461
// 如果緩存Reflector信息,放入緩存容器
return reflectorMap.computeIfAbsent(type, Reflector::new);
} else {
return new Reflector(type);
}
}
}
ObjectFactory
ObjectFactory 接口是 MyBatis 對象創(chuàng)建工廠,其默認(rèn)實現(xiàn) DefaultObjectFactory 通過構(gòu)造器反射創(chuàng)建對象,支持使用無參構(gòu)造器和有參構(gòu)造器。
Property 工具集
MyBatis 在映射文件定義 resultMap 支持如下形式:
<resultMap id="map" type="Order">
<result property="orders[0].items[0].name" column="col1"/>
<result property="orders[0].items[1].name" column="col2"/>
...
</resultMap>
orders[0].items[0].name 這樣的表達(dá)式是由 PropertyTokenizer 解析的,其構(gòu)造方法能夠?qū)Ρ磉_(dá)式進(jìn)行解析;同時還實現(xiàn)了 Iterator 接口,能夠迭代解析表達(dá)式。
public PropertyTokenizer(String fullname) {
// orders[0].items[0].name
int delim = fullname.indexOf('.');
if (delim > -1) {
// name = orders[0]
name = fullname.substring(0, delim);
// children = items[0].name
children = fullname.substring(delim + 1);
} else {
name = fullname;
children = null;
}
// orders[0]
indexedName = name;
delim = name.indexOf('[');
if (delim > -1) {
// 0
index = name.substring(delim + 1, name.length() - 1);
// order
name = name.substring(0, delim);
}
}
/**
* 是否有children表達(dá)式繼續(xù)迭代
*
* @return
*/
@Override
public boolean hasNext() {
return children != null;
}
/**
* 分解出的 . 分隔符的 children 表達(dá)式可以繼續(xù)迭代
* @return
*/
@Override
public PropertyTokenizer next() {
return new PropertyTokenizer(children);
}
PropertyNamer 可以根據(jù) getter/setter 規(guī)范解析字段名稱;PropertyCopier 則支持對有相同父類的對象,通過反射拷貝字段值。
MetaClass
MetaClass 類依賴 PropertyTokenizer 和 Reflector 查找表達(dá)式是否可以匹配 Java 對象中的字段,以及對應(yīng)字段是否有 getter/setter 方法。
/**
* 驗證傳入的表達(dá)式,是否存在指定的字段
*
* @param name
* @param builder
* @return
*/
private StringBuilder buildProperty(String name, StringBuilder builder) {
// 映射文件表達(dá)式迭代器
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) {
// 復(fù)雜表達(dá)式,如name = items[0].name,則prop.getName() = items
String propertyName = reflector.findPropertyName(prop.getName());
if (propertyName != null) {
builder.append(propertyName);
// items.
builder.append(".");
// 加載內(nèi)嵌字段類型對應(yīng)的MetaClass
MetaClass metaProp = metaClassForProperty(propertyName);
// 迭代子字段
metaProp.buildProperty(prop.getChildren(), builder);
}
} else {
// 非復(fù)雜表達(dá)式,獲取字段名,如:userid->userId
String propertyName = reflector.findPropertyName(name);
if (propertyName != null) {
builder.append(propertyName);
}
}
return builder;
}
MetaObject 與 ObjectWrapper
相對于 MetaClass 關(guān)注類信息,MetalObject 關(guān)注的是對象的信息,除了保存?zhèn)魅氲膶ο蟊旧恚€會為對象指定一個 ObjectWrapper 將對象包裝起來。ObejctWrapper 體系如下:

ObjectWrapper 的默認(rèn)實現(xiàn)包括了對 Map、Collection 和普通 JavaBean 的包裝。MyBatis 還支持通過 ObjectWrapperFactory 接口對 ObejctWrapper 進(jìn)行擴(kuò)展,生成自定義的包裝類。MetaObject 對對象的具體操作,就委托給真正的 ObjectWrapper 處理。
private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
this.originalObject = object;
this.objectFactory = objectFactory;
this.objectWrapperFactory = objectWrapperFactory;
this.reflectorFactory = reflectorFactory;
// 根據(jù)傳入object類型不同,指定不同的wrapper
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);
}
}
例如賦值操作,BeanWrapper 的實現(xiàn)如下:
@Override
public void set(PropertyTokenizer prop, Object value) {
if (prop.getIndex() != null) {
// 當(dāng)前表達(dá)式是集合,如:items[0],就需要獲取items集合對象
Object collection = resolveCollection(prop, object);
// 在集合的指定索引上賦值
setCollectionValue(prop, collection, value);
} else {
// 解析完成,通過Invoker接口做賦值操作
setBeanProperty(prop, object, value);
}
}
protected Object resolveCollection(PropertyTokenizer prop, Object object) {
if ("".equals(prop.getName())) {
return object;
} else {
// 在對象信息中查到此字段對應(yīng)的集合對象
return metaObject.getValue(prop.getName());
}
}
根據(jù) PropertyTokenizer 對象解析出的當(dāng)前字段是否存在 index 索引來判斷字段是否為集合。如果當(dāng)前字段對應(yīng)集合,則需要在對象信息中查到此字段對應(yīng)的集合對象:
public Object getValue(String name) {
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) {
// 如果表達(dá)式仍可迭代,遞歸尋找字段對應(yīng)的對象
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
return null;
} else {
return metaValue.getValue(prop.getChildren());
}
} else {
// 字段解析完成
return objectWrapper.get(prop);
}
}
如果字段是簡單類型,BeanWrapper 獲取字段對應(yīng)的對象邏輯如下:
@Override
public Object get(PropertyTokenizer prop) {
if (prop.getIndex() != null) {
// 集合類型,遞歸獲取
Object collection = resolveCollection(prop, object);
return getCollectionValue(prop, collection);
} else {
// 解析完成,反射讀取
return getBeanProperty(prop, object);
}
}
可以看到,仍然是會判斷表達(dá)式是否迭代完成,如果未解析完字段會不斷遞歸,直至找到對應(yīng)的類型。前面說到 Reflector 創(chuàng)建過程中將對字段的讀取和賦值操作通過 Invoke 接口抽象出來,針對最終獲取的字段,此時就會調(diào)用 Invoke 接口對字段反射讀取對象值:
/**
* 通過Invoker接口反射執(zhí)行讀取操作
*
* @param prop
* @param object
*/
private Object getBeanProperty(PropertyTokenizer prop, Object object) {
try {
Invoker method = metaClass.getGetInvoker(prop.getName());
try {
return method.invoke(object, NO_ARGUMENTS);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
} catch (RuntimeException e) {
throw e;
} catch (Throwable t) {
throw new ReflectionException("Could not get property '" + prop.getName() + "' from " + object.getClass() + ". Cause: " + t.toString(), t);
}
}
對象讀取完畢再通過 setCollectionValue 方法對集合指定索引進(jìn)行賦值或通過 setBeanProperty 方法對簡單類型反射賦值。MapWrapper 的操作與 BeanWrapper 大致相同,CollectionWrapper 相對更會簡單,只支持對原始集合對象進(jìn)行添加操作。
小結(jié)
MyBatis 根據(jù)自身需求,對反射 API 做了近一步封裝。其目的是簡化反射操作,為對象字段的讀取和賦值提供更好的性能。
-
org.apache.ibatis.reflection.Reflector:緩存類的字段名和 getter/setter 方法的元信息,使得反射時有更好的性能。 -
org.apache.ibatis.reflection.invoker.Invoker::用于抽象設(shè)置和讀取字段值的操作。 -
org.apache.ibatis.reflection.TypeParameterResolver:針對 Java-Type 體系的多種實現(xiàn),解析指定類中的字段、方法返回值或方法參數(shù)的類型。 -
org.apache.ibatis.reflection.DefaultReflectorFactory:默認(rèn)的 Reflector 創(chuàng)建工廠。 -
org.apache.ibatis.reflection.factory.ObjectFactory:MyBatis 對象創(chuàng)建工廠,其默認(rèn)實現(xiàn) DefaultObjectFactory 通過構(gòu)造器反射創(chuàng)建對象。 -
org.apache.ibatis.reflection.property:property 工具包,針對映射文件表達(dá)式進(jìn)行解析和 Java 對象的反射賦值。 -
org.apache.ibatis.reflection.MetaClass:依賴 PropertyTokenizer 和 Reflector 查找表達(dá)式是否可以匹配 Java 對象中的字段,以及對應(yīng)字段是否有 getter/setter 方法。 -
org.apache.ibatis.reflection.MetaObject:對原始對象進(jìn)行封裝,將對象操作委托給 ObjectWrapper 處理。 -
org.apache.ibatis.reflection.wrapper.ObjectWrapper:對象包裝類,封裝對象的讀取和賦值等操作。