Reflector簡介
Reflector反射器,每個Reflector對象對應一個類,該對象會緩存反射操作所需要的類元資訊,便于後續反射操作。
Reflector的類注釋如下(難得作者心情好,寫了兩行注釋):
This class represents a cached set of class definition information that allows for easy mapping between property names and getter/setter methods.
此類表示一組緩存的類定義資訊,可輕松在屬性名稱和getter / setter方法之間進行映射
Reflector的字段
Reflector的構造函數會對所有字段進行初始化
因為mybatis是一個orm架構,故其對反射使用最多的場景就是将封裝了請求參數的pojo拼接到對應的sql語句中,或是将sql查詢出的結果集映射到對應的pojo對象中,是以調用pojo的getter和setter方法是最頻繁的。觀察Reflector類中定義的字段可以發現,Reflector類就是為了更好的服務于ORM映射的使用場景而設計。
private final Class<?> type;//Reflector對應的類
private final String[] readablePropertyNames;//可讀屬性的名稱集合
private final String[] writablePropertyNames;//可寫屬性的名稱集合
private final Map<String, Invoker> setMethods = new HashMap<>();//所有屬性的set方法
private final Map<String, Invoker> getMethods = new HashMap<>();//所有屬性的get方法
private final Map<String, Class<?>> setTypes = new HashMap<>();//所有set方法的類型
private final Map<String, Class<?>> getTypes = new HashMap<>();//所有get方法的類型
private Constructor<?> defaultConstructor;//預設的構造函數(空參構造)
private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();//不區分大小的屬性集合
Reflector的構造函數
構造函數為Reflector類的核心方法,Reflector的構造函數會對上述所有字段進行初始化,便于後續調用,是以本文重點分析構造函數中的邏輯。
構造函數的源碼如下:
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類的構造函數源碼
設定Class
在Reflector簡介的章節中說過,Reflector對象的作用是緩存反射操作所需要的類元資訊,便于後續反射操作調用。是以類和Reflector對象是一一對應的關系,故構造函數的第一行代碼就是初始化type字段。
addDefaultConstructor
addDefaultConstructor(clazz)方法的作用是找到該類的預設空參構造器。
源碼如下:
private void addDefaultConstructor(Class<?> clazz) {
//擷取該類聲明的所有構造函數
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
//找到空參的構造器,并将其賦給defaultConstructor字段
Arrays.stream(constructors)
.filter(constructor -> constructor.getParameterTypes().length == 0)
.findAny()
.ifPresent(constructor -> this.defaultConstructor = constructor);
}
對應非lamda代碼為(未測試,僅供參考):
private void addDefaultConstructor(Class<?> clazz) {
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
for (Constructor<?> constructor : constructors)
if(constructor.getParameterTypes().length == 0){
this.defaultConstructor = constructor;
}
}
代碼邏輯簡單,不多BB直接過。
addGetMethods
addGetMethods(clazz)方法的作用找到clazz對應的類中的所有
private void addGetMethods(Class<?> clazz) {
//因為子類可能會重寫父類相同的方法,是以資料結構設計為name->List<Method>
Map<String, List<Method>> conflictingGetters = new HashMap<>();
Method[] methods = getClassMethods(clazz);
//這段lamda的意思是從數組中找到所有參數長度為0,且以get開頭的方法,并将其添加到addMethodConflict中
Arrays
.stream(methods)
.filter(m -> m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName()))
.forEach(m -> addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m));
//解析沖突的get方法
resolveGetterConflicts(conflictingetters);
}
getClassMethods
該方法的作用是從目前類開始,遞歸向上查找所有父類和接口中定義的方法
private Method[] getClassMethods(Class<?> clazz) {
Map<String, Method> uniqueMethods = new HashMap<>();
Class<?> currentClass = clazz;
//遞歸退出條件
while (currentClass != null && currentClass != Object.class) {
//将目前類中聲明的方法添加到Map集合uniqueMethods中
addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());
//擷取目前類的實作的所有接口
Class<?>[] interfaces = currentClass.getInterfaces();
//周遊将目前類所實作的接口中聲明的方法添加到Map集合uniqueMethods中
for (Class<?> anInterface : interfaces) {
addUniqueMethods(uniqueMethods, anInterface.getMethods());
}
//擷取目前類的父類,繼續向上遞歸
currentClass = currentClass.getSuperclass();
}
Collection<Method> methods = uniqueMethods.values();
return methods.toArray(new Method[0]);
}
addUniqueMethods
添加methods數組中的所有方法到map中,其中key是目前方法的簽名,mybatis自定義了方法簽名的格式,用于區分重寫方法和重載方法,自定義方法簽名的格式為:
signature = 傳回值類型#方法名:參數類型1,參數類型2,參數類型3…
addUniqueMethods方法的邏輯較為簡單,不做分析。不過需要注意的是方法并沒有将橋接方法添加到map中,關于什麼是橋接方法,可以參考文章
[]: https://www.zhihu.com/question/54895701/answer/141623158 “Java反射中method.isBridge()由來,含義和使用場景?”
private void addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods) {
for (Method currentMethod : methods) {
if (!currentMethod.isBridge()) {
String signature = getSignature(currentMethod);
if (!uniqueMethods.containsKey(signature)) {
uniqueMethods.put(signature, currentMethod);
}
}
}
}
addMethodConflict
1、調用isValidPropertyName方法判斷目前屬性名稱是否是合法的屬性名稱,比如不能是以$開頭,屬性名不等于serialVersionUID等等
2、将目前方法添加到conflictingMethods中
private void addMethodConflict(Map<String, List<Method>> conflictingMethods, String name, Method method) {
if (isValidPropertyName(name)) {
List<Method> list = conflictingMethods.computeIfAbsent(name, k -> new ArrayList<>());
list.add(method);
}
}
resolveGetterConflicts
周遊每個屬性,查找其最比對的方法。因為子類可以覆寫父類的方法,是以一個屬性,可能對應多個 getting 方法。
winner表示勝者,candidate表示候選人,代碼邏輯不複雜,直接看注釋即可。
private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
for (Entry<String, List<Method>> entry : conflictingGetters.entrySet()) {
Method winner = null;
String propName = entry.getKey();
boolean isAmbiguous = false;//是否有二義性
for (Method candidate : entry.getValue()) {
//初始情況,直接指派
if (winner == null) {
winner = candidate;
continue;
}
Class<?> winnerType = winner.getReturnType();
Class<?> candidateType = candidate.getReturnType();
if (candidateType.equals(winnerType)) {
//如果候選者的傳回值類型和勝者的傳回值類型相同,且不是布爾類型,則表示出現了二義性。
//也就是說在前面的addUniqueMethods()方法中并沒有對這兩個方法進行合并,這種情況是不允許的。
//是以将isAmbiguous标志位置為True,在稍後的代碼邏輯中會抛出對應的異常(ReflectionException) //終止程式運作
if (!boolean.class.equals(candidateType)) {
isAmbiguous = true;
break;
} else if (candidate.getName().startsWith("is")) {
//如果是boolean類型的話,屬性名為is開頭的優先級更高
winner = candidate;
}
//下面這段不了解的話可以看下面的例子
} else if (candidateType.isAssignableFrom(winnerType)) {
//如果此時candidateType是winnerType的父類,則此時不作操作
} else if (winnerType.isAssignableFrom(candidateType)) {
//如果此時winnerType是candidateType的父類,則candidate更适合
winner = candidate;
} else {
isAmbiguous = true;
break;
}
}
addGetMethod(propName, winner, isAmbiguous);
}
}
舉例
public class Father<T> {
private T nickname;
public T getNickname() {
return nickname;
}
public void setNickname(T nickname) {
this.nickname = nickname;
}
}
public class Son extends Father<String>{
private String nickname;
@Override
public String getNickname() {
return nickname;
}
@Override
public void setNickname(String nickname) {
this.nickname = nickname;
}
}
以上述兩個類為例,Father類的nickname字段的類型為Object(泛型擦除後),而Son類的nickname字段的類型為String。在出現這個情況時,上述代碼會選擇子類的該方法。
讀者可debug下面的測試類進行調試
@Test
void testCreateReflactor(){
ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
Reflector reflector = reflectorFactory.findForClass(Son.class);
Assertions.assertEquals(String.class, reflector.getGetterType("nickname"));
}
addGetMethod
最後會調用addGetMethod(String name, Method method, boolean isAmbiguous)方法将選擇出的get方法添加到目前Reflector對象的getMethods和getTypes屬性中。
代碼中的MethodInvoker和TypeParameterResolver.resolveReturnType(method, type)方法會在後續文章中進行詳解
private void addGetMethod(String name, Method method, boolean isAmbiguous) {
//當isAmbiguous為true,即出現二義性時,後續代碼會抛出ReflectionException異常,終止程式運作
MethodInvoker invoker = isAmbiguous
? new AmbiguousMethodInvoker(method, MessageFormat.format(
"Illegal overloaded getter method with ambiguous type for property ''{0}'' in class ''{1}''. This breaks the JavaBeans specification and can cause unpredictable results.",
name, method.getDeclaringClass().getName()))
: new MethodInvoker(method);
//添加到getMethods屬性中
getMethods.put(name, invoker);
//解析get方法的傳回值類型
Type returnType = TypeParameterResolver.resolveReturnType(method, type);
//添加到getTypes屬性中
getTypes.put(name, typeToClass(returnType));
}
addGetMethods方法邏輯梳理
addSetMethods
addSetMethods方法的邏輯與addGetMethods方法的邏輯相似,差異點在于resolveSetterConflicts方法,即如何選擇出合适的set方法,是以本節直接分析resolveSetterConflicts方法即可,其餘部分略略略。。。
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);
}
resolveSetterConflicts
該方法借助了getTypes和getMethods進行判斷,邏輯簡單,直接看注釋就好
private void resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) {
for (Entry<String, List<Method>> entry : conflictingSetters.entrySet()) {
String propName = entry.getKey();
List<Method> setters = entry.getValue();
Class<?> getterType = getTypes.get(propName);
boolean isGetterAmbiguous = getMethods.get(propName) instanceof AmbiguousMethodInvoker;
boolean isSetterAmbiguous = false;
Method match = null;
for (Method setter : setters) {
//getter方法傳回的類型和目前setter方法的入參類型相同,即找到最合适的set方法
if (!isGetterAmbiguous && setter.getParameterTypes()[0].equals(getterType)) {
match = setter;
break;
}
//沒有找到對應的get方法,且set方法不存在二義性,處理隻有set方法沒有get方法的情況
if (!isSetterAmbiguous) {
match = pickBetterSetter(match, setter, propName);
isSetterAmbiguous = match == null;
}
}
//如果找到合适的set方法,則将其添加到setMethods和setTypes屬性中
if (match != null) {
addSetMethod(propName, match);
}
}
}
pickBetterSetter
與resolveGetterConflicts方法相關的邏輯大同小異
private Method pickBetterSetter(Method setter1, Method setter2, String property) {
if (setter1 == null) {
return setter2;
}
Class<?> paramType1 = setter1.getParameterTypes()[0];
Class<?> paramType2 = setter2.getParameterTypes()[0];
//兩個類型進行比較取子類
if (paramType1.isAssignableFrom(paramType2)) {
return setter2;
} else if (paramType2.isAssignableFrom(paramType1)) {
return setter1;
}
MethodInvoker invoker = new AmbiguousMethodInvoker(setter1,
MessageFormat.format(
"Ambiguous setters defined for property ''{0}'' in class ''{1}'' with types ''{2}'' and ''{3}''.",
property, setter2.getDeclaringClass().getName(), paramType1.getName(), paramType2.getName()));
setMethods.put(property, invoker);
Type[] paramTypes = TypeParameterResolver.resolveParamTypes(setter1, type);
setTypes.put(property, typeToClass(paramTypes[0]));
return null;
}
addFields
遞歸添加類及其父類中定義的字段,關于字段和屬性的差別可以參考這篇文章
private void addFields(Class<?> clazz) {
//擷取clazz聲明的所有字段
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (!setMethods.containsKey(field.getName())) {
int modifiers = field.getModifiers();//擷取字段的修飾符
//不是final,也不是static的将會被添加到setMethods和setTypes字段中
if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
addSetField(field);
}
}
//添加到getMethods和getTypes字段中
if (!getMethods.containsKey(field.getName())) {
addGetField(field);
}
}
if (clazz.getSuperclass() != null) {
addFields(clazz.getSuperclass());
}
}
readablePropertyNames和writablePropertyNames屬性的初始化
readablePropertyNames數組中儲存着所有可讀屬性和字段的名稱,即定義了get方法的字段名
writablePropertyNames數組中儲存着所有可寫屬性和字段的名稱,即定義了set方法的字段名
ReflectorFactory接口
通過上面的介紹可以知道,在mybatis中,一個類就會對應一個Reflector對象。假設有100個類,就需要有100個對應的Reflector對象,如何管理這些Reflector對象是需要考慮的問題,于是mybatis的作者設計了ReflectorFactory接口并對其進行了預設實作,便于上層代碼使用。
ReflectorFactory為Reflector的工廠接口,用于建立和緩存 Reflector 對象,其接口定義如下:
public interface ReflectorFactory {
//判斷是否允許對reflector進行緩存
boolean isClassCacheEnabled();
//設定是否允許對reflector進行緩存
void setClassCacheEnabled(boolean classCacheEnabled);
//擷取type對應的Reflector對象
Reflector findForClass(Class<?> type);
}
DefaultReflectorFactory
ReflectorFactory接口隻有一個實作類,即DefaultReflectorFactory類
public class DefaultReflectorFactory implements ReflectorFactory {
//預設開啟緩存
private boolean classCacheEnabled = true;
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;
}
//如果允許緩存則直接在類中定義的ConcurrentHashMap中擷取
//如果不允許緩存則每次都會建立新的Reflector對象
@Override
public Reflector findForClass(Class<?> type) {
if (classCacheEnabled) {
//如果reflectorMap中沒有對應的Reflector對象,則會建立一個對象并将其放在reflectorMap中
return reflectorMap.computeIfAbsent(type, Reflector::new);
} else {
return new Reflector(type);
}
}
}