MyBatis源碼分析-基礎支持層反射模塊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 = new HashMap<>();
 //記錄了默認構造方法
 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,通過簽名進行過濾,將此Map轉換為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 實現類,從而實現功能上的擴展。

 

本文轉自:https://www.toutiao.com/a6613870863701770759/?tt_from=mobile_qq&utm_campaign=client_share&timestamp=1539993909&app=news_article&utm_source=mobile_qq&iid=46395736178&utm_medium=toutiao_android&group_id=6613870863701770759


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM