MyBatis反射模塊源碼分析
MyBatis 在進行參數處理、結果映射時等操作時,會涉及大量的反射操作。為了簡化這些反射相關操作,MyBatis 在 org.apache.ibatis.reflection 包下提供了專門的反射模塊,對反射操作做了近一步封裝,提供了更為簡潔的 API。
一. Reflector
MyBatis 提供 Reflector 類來緩存類的字段名和 getter/setter 方法的元信息,使得涉及反射的操作時不用再去獲取這些元信息,使操作更加便捷。使用方式是將原始類對象傳入其構造方法,生成 Reflector 對象。
public class Reflector {
private final Class<?> type;
//可讀的屬性名稱
private final String[] readablePropertyNames;
//可寫的屬性名稱
private final String[] writablePropertyNames;
//set方法的屬性
private final Map<String, Invoker> setMethods = new HashMap<>();
//get方法的屬性
private final Map<String, Invoker> getMethods = new HashMap<>();
//setter類型的列表
private final Map<String, Class<?>> setTypes = new HashMap<>();
//getter類型的列表
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);
//加入getter方法
addGetMethods(clazz);
//加入setter方法
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);
}
}
...
}
addGetMethods 和 addSetMethods 分別獲取類的所有方法,從符合 getter/setter 規范的方法中解析出字段名,并記錄方法的參數類型、返回值類型等信息:
/**
* 注意博主這個版本是3.5.5-SNAPSHOT,采用的是Java8提供的流式操作,老版本的MyBatis源碼和它不一樣,但是功能上是一致的
* @param clazz
*/
private void addGetMethods(Class<?> clazz) {
Map<String, List<Method>> conflictingGetters = new HashMap<>();
Method[] methods = getClassMethods(clazz);
Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName()))
.forEach(m -> addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m));
resolveGetterConflicts(conflictingGetters);
}
對 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 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 接口用于抽象設置和讀取字段值的操作。對于有 getter/setter 方法的字段,通過 MethodInvoker 反射執行;對應其它字段,通過 GetFieldInvoker 和 SetFieldInvoker 操作 Field 對象的 getter/setter 方法反射執行。
/**
* 用于抽象設置和讀取字段值的操作
*
* {@link MethodInvoker} 反射執行getter/setter方法
* {@link GetFieldInvoker} {@link SetFieldInvoker} 反射執行Field對象的get/set方法
*
* @author Clinton Begin
*/
public interface Invoker {
/**
* 通過反射設置或讀取字段值
*
* @param target
* @param args
* @return
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException;
/**
* 字段類型
*
* @return
*/
Class<?> getType();
}
三. TypeParameterResolver
針對 Java-Type 體系的多種實現,TypeParameterResolver 提供一系列方法來解析指定類中的字段、方法返回值或方法參數的類型。
Type 接口包含 4 個子接口和 1 個實現類:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-4RaVMfK5-1588231783205)(…/images/35.png)]
Class:原始類型ParameterizedType:泛型類型,如:List<String>TypeVariable:泛型類型變量,如:List<T>中的TGenericArrayType:組成元素是ParameterizedType或TypeVariable的數組類型,如:List<String>[]、T[]WildcardType:通配符泛型類型變量,如:List<?>中的?
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 類提供了相關的測試用例。
四. ReflectorFactory
MyBatis 還提供 ReflectorFactory 接口用于創建 Reflector 容器,其默認實現為 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) {
//如果開啟了緩存機制,則嘗試從緩存中拿,如果沒有則創建一個
return reflectorMap.computeIfAbsent(type, Reflector::new);
} else {
return new Reflector(type);
}
}
}
五. ObjectFactory
ObjectFactory 接口是 MyBatis 對象創建工廠,其默認實現 DefaultObjectFactory 通過構造器反射創建對象,支持使用無參構造器和有參構造器。
六. 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 這樣的表達式是由 PropertyTokenizer 解析的,其構造方法能夠對表達式進行解析;同時還實現了 Iterator 接口,能夠迭代解析表達式。
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);
}
}
...
}
PropertyNamer 可以根據 getter/setter 規范解析字段名稱;PropertyCopier 則支持對有相同父類的對象,通過反射拷貝字段值。
七. MetaClass
MetaClass 類依賴 PropertyTokenizer 和 Reflector 查找表達式是否可以匹配 Java 對象中的字段,以及對應字段是否有 getter/setter 方法。
八. ObjectWrapper
ObjectWrapper是對象的包裝器,提供最基本的get和set方法,不支持多級操作。ObejctWrapper 體系如下:

ObjectWrapper 的默認實現包括了對 Map、Collection 和普通 JavaBean 的包裝。MyBatis 還支持通過 ObjectWrapperFactory 接口對 ObejctWrapper 進行擴展,生成自定義的包裝類。
例如賦值操作,BeanWrapper 的實現如下:
@Override
public Object get(PropertyTokenizer prop) {
if (prop.getIndex() != null) {
// 當前表達式是集合,如:items[0],就需要獲取items集合對象
Object collection = resolveCollection(prop, object);
return getCollectionValue(prop, collection);
} else {
return getBeanProperty(prop, object);
}
}
@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());
}
}
九. MetaObject
相對于 MetaClass 關注類信息,MetalObject 關注的是對象的信息,它在內部包裝了MyBatis中五個核心的反射類。也是提供給外部使用的反射工具類,可以利用它可以讀取或者修改對象的屬性信息:
public class MetaObject {
//原始對象
private final Object originalObject;
//對象的包裝類對象
private final ObjectWrapper objectWrapper;
//對象的工廠
private final ObjectFactory objectFactory;
//對象包裝器的工廠
private final ObjectWrapperFactory objectWrapperFactory;
//反射器工廠
private final ReflectorFactory reflectorFactory;
MetaObject 對對象的具體操作,就委托給真正的 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) {
//如果對象本身已經是ObjectWrapper型,則直接賦給objectWrapper
this.objectWrapper = (ObjectWrapper) object;
} else if (objectWrapperFactory.hasWrapperFor(object)) {
//如果有包裝器,調用ObjectWrapperFactory.getWrapperFor
this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
} else if (object instanceof Map) {
//如果是Map型,返回MapWrapper
this.objectWrapper = new MapWrapper(this, (Map) object);
} else if (object instanceof Collection) {
//如果是Collection型,返回CollectionWrapper
this.objectWrapper = new CollectionWrapper(this, (Collection) object);
} else {
//除此以外,返回BeanWrapper
this.objectWrapper = new BeanWrapper(this, object);
}
}
例如賦值操作:
/**
* 設置具體值,支持多級操作
* @param name 屬性名稱,類似于OGNL表達式,如果是多級結構,直接person[0].birthdate.year即可
* @param value
*/
public void setValue(String name, Object value) {
//將name解析為PropertyTokenizer,方便進行對象的賦值
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) {
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
//如果獲取出來的設置空的MetaObject
if (value == null) {
// 如果值為空,則不會實例化子路徑
return;
} else {
//如果value不為空,則委派給ObjectWrapper.instantiatePropertyValue創建子級對象并獲取子級對象的MetaObject
metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
}
}
//繼續給子節點賦值(偽遞歸)
metaValue.setValue(prop.getChildren(), value);
} else {
//到了最后一層了,最終還是委派給ObjectWrapper.set
objectWrapper.set(prop, value);
}
}
十. 測試
由于MetaObject是MyBatis反射模塊對外提供的反射工具類,所以我們對其進行測試:
public class Area {
private String name;
private Area child;
}
/**
* 使用MetaObject工具進行多級賦值測試
*/
@Test
public void testMetaObjectForChildBean() {
//初始化對象工廠
DefaultObjectFactory objectFactory = new DefaultObjectFactory();
//通過對象工程創建 Area實例
Area area = objectFactory.create(Area.class);
//創建包裝類工廠
DefaultObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
//創建反射器工廠
DefaultReflectorFactory reflectorFactory = new DefaultReflectorFactory();
//創建MetaObject
MetaObject metaObject = MetaObject.forObject(area, objectFactory, objectWrapperFactory, reflectorFactory);
//賦值
metaObject.setValue("name","湖北省");
metaObject.setValue("child.name","武漢市");
metaObject.setValue("child.child.name","洪山區");
}


浙公網安備 33010602011771號