MyBatis原始碼分析-1-基礎支援層-反射模組-Reflector/ReflectorFactory
本文主要介紹MyBatis的反射模組是如何實現的。
MyBatis 反射的核心類Reflector,下面我先說明它的建構函式和成員變數。具體方法下面詳解。
org.apache.ibatis.reflection.Reflector
public class Reflector { private final Class<?> type; //對應的Class 型別 //可讀屬性的名稱集合,可讀屬性就是存在相應getter 方法的屬性,初始值為空數紐 private final String[] readablePropertyNames; //可寫屬性的名稱集合,可寫屬性就是存在相應setter 方法的屬性,初始值為空數紐private final String[] writeablePropertyNames; //記錄了屬性相應的setter 方法, key 是屬性名稱, value 是Invoker 物件,它是對setter 方法對應 private final Map<String, Invoker> setMethods = new HashMap<>(); //記錄了屬性相應的getter 方法, key 是屬性名稱, value 是Invoker 物件,它是對setter 方法對應 private final Map<String, Invoker> getMethods= new HashMap<>(); //記錄了屬性相應的setter 方法的引數值型別, ke y 是屬性名稱, value 是setter 方法的引數型別 private final Map<String, Class<?>> setTypes = new HashMap<>(); //記錄了屬性相應的getter 方法的返回位型別, key 是屬性名稱, value 是getter 方法的返回位型別 private final Map<String, Class<?>> getTypes = newHashMap<>(); //記錄了預設構造方法 private Constructor<?> defaultConstructor; //記錄了所有屬性名稱的集合 private Map<String, String> caseInsensitivePropertyMap = new HashMap<>(); public Reflector(Class<?> clazz) { type = clazz; //查詢clazz的無參構造方法,通過反射遍歷所有構造方法,找到構造引數集合長度為0的。 addDefaultConstructor(clazz); //處理clazz 中的getter 方法,填充getMethods 集合和getTypes 集合 addGetMethods(clazz); //處理clazz 中的set ter 方法,填充setMethods 集合和set Types 集合 addSetMethods(clazz); //處理沒有get/set的方法欄位 addFields(clazz); //初始化可讀寫的名稱集合 readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]); writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]); //初始化caseInsensitivePropertyMap ,記錄了所有大寫格式的屬性名稱 for (String propName : readablePropertyNames) { caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName); } for (String propName : writeablePropertyNames) { caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName); } } 。。。。。。。。。。。。具體程式碼先忽略,通過建構函式的呼叫慢慢滲透。 }
1:addDefaultConstructor() // 查詢clazz的無參構造方法,通過反射遍歷所有構造方法,找到構造引數集合長度為0的。
主要實現的思想是,通過 clazz.getDeclaredConstructors();獲取所有構造方法集合,然後迴圈遍歷 判斷引數長度為0的,並且建構函式許可權可控制的設為預設構造方法。
private void addDefaultConstructor(Class<?> clazz) {
Constructor<?>[] consts = clazz.getDeclaredConstructors();
for (Constructor<?> constructor : consts) {
if (constructor.getParameterTypes().length == 0) {
//判斷反射物件的控制權限 為true是可控制
if (canControlMemberAccessible()) {
try {
//設定Accessible為true後,反射可以訪問私有變數。
constructor.setAccessible(true);
} catch (Exception e) {
// Ignored. This is only a final precaution, nothing we can do.
}
}
if (constructor.isAccessible()) {
this.defaultConstructor = constructor;
}
}
}
}
2:addGetMethods(clazz)// 處理clazz 中的getter 方法,填充getMethods 集合和getTypes 集合
private void addGetMethods(Class<?> cls) { Map<String, List<Method>> conflictingGetters = new HashMap<>(); //獲取當前類以及父類中定義的所有方法的唯一簽名以及相應的Method物件。 Method[] methods = getClassMethods(cls); for (Method method : methods) { if (method.getParameterTypes().length > 0) { continue; } String name = method.getName(); //判斷如果方法明是以get開頭並且方法名長度大於3 或者 方法名是以is開頭並且長度大於2 if ((name.startsWith("get") && name.length() > 3) || (name.startsWith("is") && name.length() > 2)) { //將方法名擷取,如果是is從第二位擷取,如果是get或者set從第三位開始擷取 name = PropertyNamer.methodToProperty(name); //addMethodConflict 方法內部很簡單隻有兩行程式碼: //1:List<Method> list=conflictingGetters.computeIfAbsent(name,K->new ArrayList<>()); 這句話的意思是,在conflictingGetters 的Map中 如果key中存在name,name什麼都不做,將value返回,如果name不存在,則返回一個新的ArrayList. //2:list.add(method); 將方法物件新增到list物件中。 addMethodConflict(conflictingGetters, name, method); } } resolveGetterConflicts(conflictingGetters); }
2-1:getClassMethods(cls);//獲取當前類以及父類中定義的所有方法的唯一簽名以及相應的Method物件。
private Method[] getClassMethods(Class<?> cls) { Map<String, Method> uniqueMethods = new HashMap<>(); Class<?> currentClass = cls; while (currentClass != null && currentClass != Object.class) { //currentClass.getDeclaredMethods(),獲取當前類的所有方法 //addUniqueMethods 為每個方法生成唯一簽名,並記錄到uniqueMethods集合中 addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods()); // we also need to look for interface methods - // because the class may be abstract 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[methods.size()]); }
2-1-1: addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods()); 為每個方法生成唯一簽名,並記錄到uniqueMethods集合中
private void addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods) { for (Method currentMethod : methods) { //判斷是不是橋接方法, 橋接方法是 JDK 1.5 引入泛型後,為了使Java的泛型方法生成的位元組碼和 1.5 版本前的位元組碼相相容,由編譯器自動生成的方法 if (!currentMethod.isBridge()) { //獲取簽名 // 簽名格式為:方法返回引數#方法名:引數名 ps:多個引數用,分割 簽名樣例:String#getName:User String signature = getSignature(currentMethod); // check to see if the method is already known // if it is known, then an extended class must have // overridden a method //如果簽名存在,則不做處理,表示子類已經覆蓋了該方法。 //如果簽名不存在,則將簽名作為Key,Method作為value 新增到uniqueMethods中 if (!uniqueMethods.containsKey(signature)) { if (canControlMemberAccessible()) { try { currentMethod.setAccessible(true); } catch (Exception e) { // Ignored. This is only a final precaution, nothing we can do. } } uniqueMethods.put(signature, currentMethod); } } } }
2-2: resolveGetterConflicts(conflictingGetters);;//在2-1中返回的方法可能存在,兩個相同的方法名稱,因為當子類實現父類方法時且引數不同,此時2-1生成的簽名是不同的生成簽名的規則是 方法返回值#方法名#引數名,那麼就會返回兩個相同的方法名。 resolveGetterConflicts方法會對這種覆寫的情況進行處理,同時將處理後的getter方法記錄到getMethods集合中,將其返回值型別填充到getTypes集合中。 內部實現主要是兩個for迴圈,迴圈比較方法名稱相同的情況下,返回值不同的情況下,拿第二個當最終想要的Method。
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; } 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")) { winner = candidate; } } else if (candidateType.isAssignableFrom(winnerType)) { // OK getter type is descendant } 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."); } } addGetMethod(propName, winner); } }
總結一下addGetMethods(clazz)方法 addSetMethods(clazz)大致相同:
首先建立:
Map<String, List<Method>> conflictingGetters = new HashMap<>();
1:獲取子類和父類的所有方法。 獲取方法是 先生成唯一簽名,唯一簽名規則是方法返回值#方法名:方法引數1,方法引數2 。 根據簽名作為key,method物件作為value生成Map,通過簽名進行過濾,將此M,ap轉換為List返回。
2:迴圈遍歷Map,找到符合條件的方法名,is開頭或者get開頭的,將方法名擷取,擷取後的方法名作為key,List<Method>作為value,放入到conflictingGetters中。
3:由於子類存在實現父類方法,且返回值不同的情況,導致用一方法名 可能有不同的Method ,第三步 resolveGetterConflicts方法會對這種覆寫的情況進行處理,同時將處理後的getter方法記錄到getMethods集合中,將其返回值型別填充到getTypes集合中。
Reflector Factory 介面主要實現了對Reflector 物件的建立和快取,有三個方法:該介面定義如下:
public interface ReflectorFactory { boolean isClassCacheEnabled(); //檢測該ReflectorFactory物件是否會快取Reflector物件 void setClassCacheEnabled(boolean classCacheEnabled);//設定是否快取Reflector物件 Reflector findForClass(Class<?> type); //建立指定class對應的Reflector物件 }
Reflector Factory的實現是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; } @Override public Reflector findForClass(Class<?> type) { //如果開啟快取,Reflector物件從ConcurrentMap<Class<?>, Reflector> 取出。 if (classCacheEnabled) { // synchronized (type) removed see issue #461 return reflectorMap.computeIfAbsent(type, Reflector::new); } else {//沒開啟快取,重新建立。 return new Reflector(type); } } }
DefaultReflectorFactory 的快取是通過ConcurrentMap來實現的,如果開啟了快取,那麼就從ConcurrentMap取Reflector,如果沒有開啟,就新建Reflector.
除了使用MyBatis 提供的DefaultReflectorFactory 實現,我們還可以在mybatis-config .xml中配置自定義的ReflectorFactory 實現類,從而實現功能上的擴充套件。