MyBatis
在進行參數處理、結果映射時等操作時,會涉及大量的反射操作。爲了簡化這些反射相關操作,MyBatis
在 org.apache.ibatis.reflection
包下提供了專門的反射模塊,對反射操作做了近一步封裝,提供了更爲簡潔的 API
。
一、JavaBean規範
JavaBean 具有如下特徵:
- 所有的屬性都是私有的(通過 getter 和 setter 訪問)
- 擁有公有的無參構造函數
- 提供 setter/getter
- 實現 Serializable 接口
二、Reflector 和 ReflectorFactory
2.1 Reflector
出於性能方面的考慮,Mybatis 不是等到使用的時候去解析 XML/反射類,而是爲每一個類提供了反射器類 Reflector ,該類中存儲了反射需要使用的類的元信息。
MyBatis
提供 Reflector
類來緩存類的字段名和 getter/setter 方法的元信息,使得反射時有更好的性能。使用方式是將原始類對象傳入其構造方法,生成 Reflector
對象。
Reflector 是 MyBatis 中反射模塊的基礎,每個 Reflector 對象都對應一個類,在 Reflector 中 緩存了反射操作需要使用的類的元信息。 Reflector 中各個字段的含義如下:
private final Class<?> type;//對於class的類型
//可讀屬性的名稱集合,存在get方法即可讀
private final String[] readablePropertyNames;
//可寫屬性的名稱集合,存在set方法即可寫
private final String[] writablePropertyNames;
//記錄了屬性相應的 setter 方法, key 是屬性名稱, value 是 Invoker 對象,它是對 setter 方法對應
private final Map<String, Invoker> setMethods = new HashMap<>();
//屬性相應的 getter 方法集合, key 是屬性名稱, value 也是 Invoker 對象
private final Map<String, Invoker> getMethods = new HashMap<>();
//記錄了屬性相應的 setter 方法的參數值類型, key 是屬性名稱, value 是 setter 方法的參數類型
private final Map<String, Class<?>> setTypes = new HashMap<>();
//記錄 了屬性相應的 getter 方法的返回位類型, key 是屬性名稱, value 是 getter 方法的返回位類型
private final Map<String, Class<?>> getTypes = new HashMap<>();
//默認構造方法
private Constructor<?> defaultConstructor;
//記錄所有屬性的名稱集合
private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();
在 Reflector 的構造方法中會解析指定的 Class 對象,並填充上述集合,具體實現如下:
public Reflector(Class<?> clazz) {
type = clazz;//初始化type對象
//查找 clazz 的默認構造方法(元參構造方法),具體實現是通過反射遙歷所有構造方法,代碼並不複雜
addDefaultConstructor(clazz);
//處理clazz中的get方法信息,填充getMethods、getTypes
addGetMethods(clazz);
//處理clazz中的set方法信息,填充setMethods、setTypes
addSetMethods(clazz);
//處理沒有get、set方法的屬性
addFields(clazz);
//根據get、set方法初始化可讀屬性集合和可寫屬性集合
readablePropertyNames = getMethods.keySet().toArray(new String[0]);
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);
}
}
從上面類的屬性中,我們可以看出:
一個反射器 Reflector 對應着一個 Class 對象
記錄了默認構造函數
其餘的是屬性及其 setter/gettter 相關
對於一個屬性(只有有 setter/getter 才能被稱之爲屬性)
(1)如果是可讀的(有 getter 方法),則 Reflector 會將其及其方法處理後放入對應的可讀相關的集合中;
(2)如果是可寫的(有 setter 方法),則 Reflector 會將其及其方法處理後放入對應的可寫相關的集合中;
(3)最後使用 Map<String, String> caseInsensitivePropertyMap 記錄所有的屬性。
addGetMethods
和 addSetMethods
分別獲取類的所有方法,從符合 getter/setter
規範的方法中解析出字段名,並記錄方法的參數類型、返回值類型等信息:
private void addGetMethods(Class<?> clazz) {
//字段名-get方法
Map<String, List<Method>> conflictingGetters = new HashMap<>();
//獲取類的所有方法,及其實現接口的方法,並根據方法簽名去重
Method[] methods = getClassMethods(clazz);
//取get 方法 條件是入參爲空,且已get開頭
Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName()))
.forEach(m -> addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m));
resolveGetterConflicts(conflictingGetters);
}
public static boolean isGetter(String name) {
return (name.startsWith("get") && name.length() > 3) || (name.startsWith("is") && name.length() > 2);
}
private void addSetMethods(Class<?> clazz) {
Map<String, List<Method>> conflictingSetters = new HashMap<>();
Method[] methods = getClassMethods(clazz);
Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 1 && PropertyNamer.isSetter(m.getName()))
.forEach(m -> addMethodConflict(conflictingSetters, PropertyNamer.methodToProperty(m.getName()), m));
resolveSetterConflicts(conflictingSetters);
}
對 getter/setter
方法進行去重是通過類似 java.lang.String#getSignature:java.lang.reflect.Method
的方法簽名來實現的,如果子類在實現過程中,參數、返回值使用了不同的類型(使用原類型的子類),則會導致方法簽名不一致,同一字段就會對應不同的 getter/setter
方法,因此需要進行去重
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;
}
// 字段對應了多個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,優先取is開頭的
winner = candidate;
}
} else if (candidateType.isAssignableFrom(winnerType)) {
// OK getter type is descendant
// 可能會出現接口中的方法返回值是List,子類實現方法返回值是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.");
}
}
// 記錄字段名對應的get方法對象和返回值類型
addGetMethod(propName, winner);
}
}
去重的方式是使用更規範的方法以及使用子類的方法。在確認字段名對應的唯一 getter/setter
方法後,記錄方法名對應的方法、參數、返回值等信息。MethodInvoker
可用於調用 Method
類的 invoke
方法來執行 getter/setter
方法(addSetMethods
記錄映射關係的方式與 addGetMethods
大致相同)。
private void addGetMethod(String name, Method method) {
// 過濾$開頭、serialVersionUID的get方法和getClass()方法
if (isValidPropertyName(name)) {
// 字段名-對應get方法的MethodInvoker對象
getMethods.put(name, new MethodInvoker(method));
Type returnType = TypeParameterResolver.resolveReturnType(method, type);
// 字段名-運行時方法的真正返回類型
getTypes.put(name, typeToClass(returnType));
}
}
接下來會執行 addFields
方法,此方法針對沒有 getter/setter
方法的字段,通過包裝爲 SetFieldInvoker
在需要時通過 Field
對象的反射來設置和讀取字段值。
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());
}
}
2.2 Invoker
Invoker
接口用於抽象設置和讀取字段值的操作。對於有 getter/setter
方法的字段,通過 MethodInvoker
反射執行;對應其它字段,通過 GetFieldInvoker
和 SetFieldInvoker
操作 Field
對象的 getter/setter
方法反射執行。
public interface Invoker {
/**
* 通過反射設置或讀取字段值
*
* @param target
* @param args
* @return
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException;
/**
* 字段類型
*
* @return
*/
Class<?> getType();
}
- MethodInvoker:有
getter/setter
方法的字段,方法的 Invoker - GetFieldInvoker:如果沒有
getter
,則使用該方法,通過Filed
類直接讀取成員變量的值 - SetFieldInvoker:如果沒有
setter
,則使用該方法,通過Filed
類直接設置成員變量的值
2.3 TypeParameterResolver
在開始介紹 TypeParameterResolver 之前, 先簡單介紹一下 Type 接口的基礎知識。 Type 是所有類型的父接口,它有四個子接口和一個實現類。
Class
:原始類型,Class 類的對象表示 JVM 中的一個類或接口, 每個 Java 類在 JVM 裏都表現爲一個 Class 對象。ParameterizedType
:泛型類型,如:List<String>
TypeVariable
:泛型類型變量,如:List<T>
中的T
GenericArrayType
:組成元素是ParameterizedType
或TypeVariable
的數組類型,如:List<String>[]
、T[]
WildcardType
:通配符泛型類型變量,如:List<?>
中的 ?
在對 Reflector 的 分析過程中,我們看到了 TypeParameterResolver 的身影,它是一個工具類,提供了一系列靜態 方法來解析指定類中的宇段、方法返回值或方法參數的類型。 TypeParameterResolver 中各個靜 態方法之間的調用關係大致如圖 所示,爲保持清晰,其中遞歸調用沒有表現出來,在後面 的代碼分析過程中會進行強調,
TypeParameterResolver
分別提供 resolveFieldType
、resolveReturnType
、resolveParamTypes
方法用於解析字段類型、方法返回值類型和方法入參類型,這些方法均調用 resolveType
來獲取類型信息:
/**
* 獲取類型信息
*
* @param type 根據是否有泛型信息簽名選擇傳入泛型類型或簡單類型
* @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 數組類型
return resolveGenericArrayType((GenericArrayType) type, srcType, declaringClass);
} else {
// 原始類型,直接返回
return type;
}
}
resolveTypeVar
用於解析泛型類型變量參數類型,如果字段或方法在當前類中聲明,則返回泛型類型的上界或 Object
類型;如 果在父類中聲明,則遞歸解析父類;父類也無法解析,則遞歸解析實現的接口
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) {
// 字段就是在當前引用類中聲明的
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;
}
通過調用 scanSuperTypes
實現遞歸解析:
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) {
// 字段/方法在當前父類中聲明
for (int i = 0; i < parentTypeVars.length; i++) {
if (typeVar == parentTypeVars[i]) {
// 使用變量對應位置的真正類型(可能已經被替換),如父類 A<T>,子類 B extends A<String>,則返回String
return parentAsType.getActualTypeArguments()[i];
}
}
}
// 字段/方法聲明的類是當前父類的父類,繼續遞歸
if (declaringClass.isAssignableFrom(parentAsClass)) {
return resolveTypeVar(typeVar, parentAsType, declaringClass);
}
} else if (superclass instanceof Class && declaringClass.isAssignableFrom((Class<?>) superclass)) {
// 父類是原始類型,繼續遞歸父類
return resolveTypeVar(typeVar, superclass, declaringClass);
}
return null;
}
解析方法返回值和方法參數的邏輯大致與解析字段類型相同,MyBatis
源碼的TypeParameterResolverTest
類提供了相關的測試用例。
2.4 ReflectorFactory
看名稱,工廠方法,是爲了創建個緩存 Reflector。MyBatis
還提供 ReflectorFactory
接口用於實現 Reflector
容器,其默認實現爲 DefaultReflectorFactory
,其中可以使用 classCacheEnabled
屬性來配置是否使用緩存。
/**
* 創建Reflector的工廠接口
*/
public interface ReflectorFactory {
/**
* 檢測 ReflectorFactory 對象是否會緩存 Reflector 對象
*/
boolean isClassCacheEnabled();
/**
* 設置是否緩存
*/
void setClassCacheEnabled(boolean classCacheEnabled);
/**
* 緩存中查找 Class 對應的 Reflector 對象,找不到則創建
*/
Reflector findForClass(Class<?> type);
}
mybatis 爲我們提供了該方法的默認實現 DefaultReflectorFactory
。該類的實現很簡單,就是通過 ConcurrentMap<Class<?>, Reflector>
對 Reflector
進行緩存。
2.5 ObjectFactory
ObjectFactory
接口是 MyBatis
對象創建工廠,其默認實現 DefaultObjectFactory
通過構造器反射創建對象,支持使用無參構造器和有參構造器。
MyBatis 中有很多模塊會使用到 ObjectFactory 接口,該接口提供了多個 create()方法的重載, 通過這些 create()方法可以創建指定類型的對象。 ObjectFactory 接口的定義如下:
public interface ObjectFactory {
/**
* 設置配置信息
* @param properties
*/
default void setProperties(Properties properties) {
// NOP
}
//通過無參構造器創建對象
<T> T create(Class<T> type);
//根據參數列表,從指定類型中選擇合適的構造器創建對象
<T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs);
///檢測指定類型是否爲集合類型,主妥處理 java.util.Collection 及其子類
<T> boolean isCollection(Class<T> type);
}
DefaultObjectFactory是MyBatis提供的 ObjectFactory接口的唯一實現,它是一個反射工廠, 其 create()方法通過調用 instantiateClass()方法實現。 DefaultObjectFactory.instantiateClass()方法會 根據傳入的參數列表選擇合適的構造函數實例化對象,具體實現如下:
public class DefaultObjectFactory implements ObjectFactory, Serializable {
private static final long serialVersionUID = -8855120656740914948L;
//通過無參構造器創建對象
@Override
public <T> T create(Class<T> type) {
return create(type, null, null);
}
@SuppressWarnings("unchecked")
@Override
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
//對象類型
Class<?> classToCreate = resolveInterface(type);
// we know types are assignable
return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
}
//最終創建對象的方法
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();
try {
//創建對象
return constructor.newInstance();
} catch (IllegalAccessException e) {
if (Reflector.canControlMemberAccessible()) {
constructor.setAccessible(true);
return constructor.newInstance();
} else {
throw e;
}
}
}
//根據指定的參數列表查找構造函數,並實例化對象
constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
try {
return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
} catch (IllegalAccessException e) {
if (Reflector.canControlMemberAccessible()) {
constructor.setAccessible(true);
return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
} else {
throw e;
}
}
} catch (Exception e) {
String argTypes = Optional.ofNullable(constructorArgTypes).orElseGet(Collections::emptyList)
.stream().map(Class::getSimpleName).collect(Collectors.joining(","));
String argValues = Optional.ofNullable(constructorArgs).orElseGet(Collections::emptyList)
.stream().map(String::valueOf).collect(Collectors.joining(","));
throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
}
}
protected Class<?> resolveInterface(Class<?> type) {
Class<?> classToCreate;
if (type == List.class || type == Collection.class || type == Iterable.class) {
classToCreate = ArrayList.class;
} else if (type == Map.class) {
classToCreate = HashMap.class;
} else if (type == SortedSet.class) { // issue #510 Collections Support
classToCreate = TreeSet.class;
} else if (type == Set.class) {
classToCreate = HashSet.class;
} else {
classToCreate = type;
}
return classToCreate;
}
@Override
public <T> boolean isCollection(Class<T> type) {
return Collection.class.isAssignableFrom(type);
}
}
2.6 Property 工具集
反射模塊中使用到的三個屬性工具類,分別是 PropertyTokenizer 、 PropertyNamer
、PropertyCopier。
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>
rders[0].items[0].name
這樣的表達式是由 PropertyTokenizer
解析的,其構造方法能夠對錶達式進行解析;同時還實現了 Iterator
接口,能夠迭代解析表達式。
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表達式繼續迭代
*
* @return
*/
@Override
public boolean hasNext() {
return children != null;
}
/**
* 分解出的 . 分隔符的 children 表達式可以繼續迭代
* @return
*/
@Override
public PropertyTokenizer next() {
return new PropertyTokenizer(children);
}
PropertyNamer
可以根據 getter/setter
規範解析字段名稱;PropertyCopier
則支持對有相同父類的對象,通過反射拷貝字段值。
2.7 MetaClass
MetaClass 通過 Reflector 和 PropertyTokenizer 組合使用, 實現了對複雜的屬性表達式的解 析,並實現了獲取指定屬性描述信息的功能。
Reflector 實現了實體類元信息的封裝,但是類中的成員變量是類的情況沒有進行處理。
而 MetaClass 通過 ReflectorFactory 類型的成員變量,實現了實體類中成員變量是類情況的處理,通過與屬性工具類的結合,實現了對複雜表達式的解析和實現了獲取指定描述信息的功能。
其成員變量:
public class MetaClass {
//ReflectorFactory 對象,用於緩存 Reflector 對象
private ReflectorFactory reflectorFactory;
//在創建 MetaClass 時會指定一個類,該 Reflector 對象會用於記錄該類相關的元信息
private Reflector reflector;
MetaClass 的構造函數中會爲指定的 Class 創建相應的 Reflector 對象,井用其初始化 MetaClass.reflector 字段
MetaClass 構造函數是私有的,通過靜態方法構建對象
//私有化構造函數
private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
this.reflectorFactory = reflectorFactory;
//創建reflector對象
this.reflector = reflectorFactory.findForClass(type);
}
//使用靜態方法創建 MetaClass 對象
public static MetaClass forClass(Class<?> type, ReflectorFactory reflectorFactory) {
return new MetaClass(type, reflectorFactory);
}
MetaClass 中比較重要的是 findProperty()方法,它是通過調用 MetaClass. buildProperty()方法 實現的,而 buildProperty()方法會通過 PropertyTokenizer解析複雜的屬性表達式,具體實現如下:
public String findProperty(String name) {
///委託給 buildProperty()方法實現
StringBuilder prop = buildProperty(name, new StringBuilder());
return prop.length() > 0 ? prop.toString() : null;
}
/**
* 解析複雜的屬性表達式,例如:<result property="orders[0].items[0].name"column="item1"/> 中 property="orders[0].items[0].name"
* 案例:
* @param name tele.name
* @param builder
* @return
*/
private StringBuilder buildProperty(String name, StringBuilder builder) {
//構造兼解析,name:tele.name
PropertyTokenizer prop = new PropertyTokenizer(name);//映射文件表達式迭代器
if (prop.hasNext()) {
//複雜表達式 //prop.getName():tele propertyName:tele
String propertyName = reflector.findPropertyName(prop.getName());
if (propertyName != null) {
builder.append(propertyName);
builder.append(".");
// 加載內嵌字段類型對應的MetaClass
MetaClass metaProp = metaClassForProperty(propertyName);
//遞歸解析 PropertyTokenizer.children 字段,並將解析結果添加到 builder 中保存
metaProp.buildProperty(prop.getChildren(), builder);
}
} else {
String propertyName = reflector.findPropertyName(name);
if (propertyName != null) {
builder.append(propertyName);
}
}
return builder;
}
理解了這個方法(遞歸,該類中有很多類似的),就可以很好的對這個類進行理解,以查找(richType.richProperty)爲例:
- 通過 PropertyTokenizer 對錶達式進行解析, 得到當前的 name=richType, children=richProperty
- 從 reflector 中查找該 richType 屬性
- 將 richType 添加到 builder 中
- 使用 metaClassForProperty 創建 richType 的 MetaClass
- 遞歸調用自身來處理子表達式
- 退出的條件就是沒有子表達式。這個是爲了,我們類中有成員變量是類,我們可以通過其找到他們的所有類及其屬性。
注意,在此過程中,ReflectorFactory 一直是同一個,而其內部緩存了多個 Reflector 對象
2.8 ObjectWrapper
MetaClass 是 MyBatis 對類級別的元信息的封裝和處理,下面來看 MyBatis 對對象級別的元 信息的處理。 ObjectWrapper 接口是對對象的包裝,抽象了對象的屬性信息,它定義了一系列查 詢對象屬性信息的方法,以及更新屬性的方法。
相對於 MetaClass
關注類信息,MetalObject
關注的是對象的信息,除了保存傳入的對象本身,還會爲對象指定一個 ObjectWrapper
將對象包裝起來。ObejctWrapper
體系如下:
ObjectWrapper
的默認實現包括了對 Map
、Collection
和普通 JavaBean
的包裝。MyBatis
還支持通過 ObjectWrapperFactory
接口對 ObejctWrapper
進行擴展,生成自定義的包裝類。MetaObject
對對象的具體操作,就委託給真正的 ObjectWrapper
處理。
public interface ObjectWrapper {
//如果ObjectWrapper 中封裝的是普通的 Bean 對象,則調用相應屬性的相應 getter 方法,
//如採封裝的是集合類,則獲取指定 key 或下標對應的 value 位
Object get(PropertyTokenizer prop);
//如果 ObjectWrapper 中封裝的是普通的 Bean 對象, 則調用相應屬性的相應 setter 方法,
// 如果封裝的是集合類,則設置指定 key 或下標對應的 value 值
void set(PropertyTokenizer prop, Object value);
//查找屬性表達式指定的屬性,第二個參數表示是否忽略屬性表達式中的下畫線
String findProperty(String name, boolean useCamelCaseMapping);
String[] getGetterNames();//查找可寫屬性的名稱集合
String[] getSetterNames();//查找可讀屬性的名稱集合
//解析屬性表達式指定屬性的 setter 方法的參數類型
Class<?> getSetterType(String name);//
//解析屬性表達式指定屬性的 getter 方法的返回值類型
Class<?> getGetterType(String name);
//判斷屬性表達式指定屬性是否有 getter/setter 方法
boolean hasSetter(String name);
boolean hasGetter(String name);
//爲屬性表達式指定的屬性創建相應的 MetaObject 對象
MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory);
boolean isCollection();
void add(Object element);
<E> void addAll(List<E> element);
}
BaseWrapper 是一個實現了 ObjectWrapper 接口的抽象類, 其中封裝了 MetaObject 對象,並 提供了三個常用的方法供其子類使用,
BaseWrapper. resolveCollection()方法會調用 MetaObject. getValue()方法法,它會解析屬性表達 式井獲取指定的屬性, MetaObject.getValue()方法的實現在後面詳細介紹。
BaseWrapper.getCollectionValue()方法和 setCollectionValue()方法會解析屬性表達式的索引 信息,然後獲取/設置對應項。這兩個方法的實現類似,這裏只分析getCollectionValue ()方法
protected Object getCollectionValue(PropertyTokenizer prop, Object collection) {
if (collection instanceof Map) {//如果是 Map 類型,如l index 爲 key
return ((Map) collection).get(prop.getIndex());
} else {
int i = Integer.parseInt(prop.getIndex());//如果是其他集合類型,則 index 爲下標
if (collection instanceof List) {
return ((List) collection).get(i);
} else if (collection instanceof Object[]) {
return ((Object[]) collection)[i];
} else if (collection instanceof char[]) {
return ((char[]) collection)[i];
} else if (collection instanceof boolean[]) {
..........................
} else {
throw new ReflectionException("The '" + prop.getName() + "' property of " + collection + " is not a List or Array.");
}
}
}
BeanWrapper 繼承了 BaseWrapper 抽象類,其中封裝了一個 JavaBean對象以及該 JavaBean 類相應的 MetaClass 對象,當然,還有從 BaseWrapper 繼承下來的、該 JavaBean 對象相應的 MetaObject 對象。
例如賦值操作
@Override
public void set(PropertyTokenizer prop, Object value) {
if (prop.getIndex() != null) {
// 當前表達式是集合,如: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 {
// 在對象信息中查到此字段對應的集合對象
return metaObject.getValue(prop.getName());
}
}
根據 PropertyTokenizer
對象解析出的當前字段是否存在 index
索引來判斷字段是否爲集合。如果當前字段對應集合,則需要在對象信息中查到此字段對應的集合對象:
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);
}
}
如果字段是簡單類型,BeanWrapper
獲取字段對應的對象邏輯如下:
@Override
public Object get(PropertyTokenizer prop) {
if (prop.getIndex() != null) {
// 集合類型,遞歸獲取
Object collection = resolveCollection(prop, object);
return getCollectionValue(prop, collection);
} else {
// 解析完成,反射讀取
return getBeanProperty(prop, object);
}
}
可以看到,仍然是會判斷表達式是否迭代完成,如果未解析完字段會不斷遞歸,直至找到對應的類型。前面說到 Reflector
創建過程中將對字段的讀取和賦值操作通過 Invoke
接口抽象出來,針對最終獲取的字段,此時就會調用 Invoke
接口對字段反射讀取對象值:
/**
* 通過Invoker接口反射執行讀取操作
*
* @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
方法對集合指定索引進行賦值或通過 setBeanProperty
方法對簡單類型反射賦值。MapWrapper
的操作與 BeanWrapper
大致相同,CollectionWrapper
相對更會簡單,只支持對原始集合對象進行添加操作。
2.9 MetaObject
ObjectWrapper 提供了獲取/設置對象中指 定的屬性值、檢測 ge出r/setter 等常用功能,但是 ObjectWrapper 只是這些功能的最後一站,我 們省略了對屬性表達式解析過程的介紹,而該解析過程是在 MetaObject 中實現的。
MetaObject中的屬性如下:
//原始 JavaBean 對象
private final Object originalObject;
//ObjectWrapper 對象,其中封裝了originalObject對象
private final ObjectWrapper objectWrapper;
//負責實例化 originalObject 的工廠對象,
private final ObjectFactory objectFactory;
//負責創建 ObjectWrapper 的工廠對象
private final ObjectWrapperFactory objectWrapperFactory;
//用於創建並緩存 Reflector 對象的工廠對象,
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;
// 根據傳入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);
}
}
// MetaObject 的構造方法是 private 修改的,只能通過 forObj ect ()這個靜態方法創建 MetaObject 對象
public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
if (object == null) {
return SystemMetaObject.NULL_META_OBJECT;
} else {
return new MetaObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
}
}
三、小結
MyBatis
根據自身需求,對反射 API
做了近一步封裝。其目的是簡化反射操作,爲對象字段的讀取和賦值提供更好的性能。
org.apache.ibatis.reflection.Reflector
:緩存類的字段名和 getter/setter 方法的元信息,使得反射時有更好的性能。org.apache.ibatis.reflection.invoker.Invoker:
:用於抽象設置和讀取字段值的操作。org.apache.ibatis.reflection.TypeParameterResolver
:針對 Java-Type 體系的多種實現,解析指定類中的字段、方法返回值或方法參數的類型。org.apache.ibatis.reflection.DefaultReflectorFactory
:默認的 Reflector 創建工廠。org.apache.ibatis.reflection.factory.ObjectFactory
:MyBatis 對象創建工廠,其默認實現 DefaultObjectFactory 通過構造器反射創建對象。org.apache.ibatis.reflection.property
:property 工具包,針對映射文件表達式進行解析和 Java 對象的反射賦值。org.apache.ibatis.reflection.MetaClass
:依賴 PropertyTokenizer 和 Reflector 查找表達式是否可以匹配 Java 對象中的字段,以及對應字段是否有 getter/setter 方法。org.apache.ibatis.reflection.MetaObject
:對原始對象進行封裝,將對象操作委託給 ObjectWrapper 處理。org.apache.ibatis.reflection.wrapper.ObjectWrapper
:對象包裝類,封裝對象的讀取和賦值等操作。
四、使用實例
ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
//使用Reflector讀取類元信息
Reflector findForClass = reflectorFactory.findForClass(TUser.class);
Constructor<?> defaultConstructor = findForClass.getDefaultConstructor();
String[] getablePropertyNames = findForClass.getGetablePropertyNames();
String[] setablePropertyNames = findForClass.getSetablePropertyNames();
System.out.println(defaultConstructor.getName());
System.out.println(Arrays.toString(getablePropertyNames));
System.out.println(Arrays.toString(setablePropertyNames));
//反射工具類初始化
ObjectFactory objectFactory = new DefaultObjectFactory();
TUser user = objectFactory.create(TUser.class);
ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
MetaObject metaObject = MetaObject.forObject(user, objectFactory, objectWrapperFactory, reflectorFactory);
ObjectWrapper wrapperForUser = new BeanWrapper(metaObject, user);
String[] getterNames = wrapperForUser.getGetterNames();
String[] setterNames = wrapperForUser.getSetterNames();
System.out.println(Arrays.toString(getterNames));
System.out.println(Arrays.toString(setterNames));
PropertyTokenizer prop = new PropertyTokenizer("userName");
wrapperForUser.set(prop, "lison");
System.out.println(user);
//模擬數據庫行數據轉化成對象
//1.模擬從數據庫讀取數據
Map<String, Object> dbResult = new HashMap<>();
dbResult.put("id", 1);
dbResult.put("user_name", "lison");
dbResult.put("real_name", "李曉宇");
TPosition tp = new TPosition();
tp.setId(1);
dbResult.put("position_id", tp);
//2.模擬映射關係
Map<String, String> mapper = new HashMap<String, String>();
mapper.put("id", "id");
mapper.put("userName", "user_name");
mapper.put("realName", "real_name");
mapper.put("position", "position_id");
//3.使用反射工具類將行數據轉換成pojo
BeanWrapper objectWrapper = (BeanWrapper) metaObject.getObjectWrapper();
Set<Entry<String, String>> entrySet = mapper.entrySet();
for (Entry<String, String> colInfo : entrySet) {
String propName = colInfo.getKey();
Object propValue = dbResult.get(colInfo.getValue());
PropertyTokenizer proTokenizer = new PropertyTokenizer(propName);
objectWrapper.set(proTokenizer, propValue);
}
System.out.println(metaObject.getOriginalObject());
}