前沿編程
前文分析了mybatis的日誌包,緩存包,數據源包。源碼實在有點難頂,在分析反射包時,花費了較多時間。廢話很少說,開始源碼之路。api
反射包feflection在mybatis路徑以下:緩存
源碼解析數據結構
1 property包-主要對類的屬性進行操做的工具包mybatis
1.1 PropertyCopier包利用反射類Filed進行屬性複製架構
// 該類做用將sourceBean與destinationBean相同屬性名的屬性進行值複製 public class PropertyCopier { // 屬性複製 public static void copyBeanProperties(Class<?> type, Object sourceBean, Object destinationBean) { Class<?> parent = type; while (parent != null) { final Field[] fields = parent.getDeclaredFields(); // 獲取該類的全部屬性 for(Field field : fields) { try { field.setAccessible(true); // 設置該屬性的訪問權限(包括私有屬性) field.set(destinationBean, field.get(sourceBean)); // 此處調用2個方法,filed.get(objectA) 獲取objectA中的filed屬性值. filed.set(objectB,value) 將value值賦值給ObjectB的filed屬性 } catch (Exception e) { // 異常直接忽略掉(對於非公共屬性直接忽略) // Nothing useful to do, will only fail on final fields, which will be ignored. } } parent = parent.getSuperclass(); // 獲取父類,循環複製父類屬性 } } }
該類主要功能是將sourceBean與destinationBean相同屬性名的屬性進行值複製,是一個屬性工具類。app
1.2 PropertyNamer根據方法名獲取屬性名稱函數
public class PropertyNamer { // 獲取getxxx,isxxx,setxxx後的xxx屬性 public static String methodToProperty(String name) { if (name.startsWith("is")) { name = name.substring(2); } else if (name.startsWith("get") || name.startsWith("set")) { name = name.substring(3); } else { throw new ReflectionException("Error parsing property name '" + name + "'. Didn't start with 'is', 'get' or 'set'."); } // 此處咱們默認使用駝峯命名,如setName,那獲取的屬性名應爲name而不是Name if (name.length() == 1 || (name.length() > 1 && !Character.isUpperCase(name.charAt(1)))) { name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1); } return name; }
該類主要做用是從set,is,get方法中獲取屬性,是一個屬性工具類。工具
1.3 PropertyTokenizer解析屬性集合,此處使用迭代器模式this
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); } }
此方法比較簡單,舉個例子。如咱們要解析group[0].user[0].name這一串字符,那通過一次迭代獲取以下結構
children = user[0].name
indexedName = group[0]
index = 0
name = group
使用迭代器方法hasNext()判斷children是否爲null,若不爲null,則繼續解析。此方法比較重要,在後文中對關於複雜屬性的解析,都使用了此類,須要重要理解。
2. Invoker包分析 - 主要對反射類Filed,Method方法進行封裝
2.1 執行器接口(將設置屬性,獲取屬性,方法執行全都用Invoker進行封裝,充分體現了面向接口編程)
public interface Invoker { // 執行方法 Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException; Class<?> getType(); }
提供對外通用接口,具體執行器需實現此接口。
2.2 獲取對象屬性的執行器
public class GetFieldInvoker implements Invoker { public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException { return field.get(target); // 調用反射類Filed.get()方法,獲取對象屬性 } }
GetFieldInvoker內部封裝了Filed.get()方法獲取對象屬性。
2.3 設置對象屬性執行器
public class SetFieldInvoker implements Invoker { public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException { field.set(target, args[0]); // 調用Filed.set()方法,設置對象屬性 return null; } }
SetFieldInvoker內部封裝了Filed.set()方法設置對象屬性。
2.4 對象方法執行器
public class MethodInvoker implements Invoker { private Class<?> type; private Method method; public MethodInvoker(Method method) { this.method = method; if (method.getParameterTypes().length == 1) { type = method.getParameterTypes()[0]; // 得到方法參數列表中的第一個參數類型 } else { type = method.getReturnType(); // 不然獲取方法的返回類型 } } public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException { return method.invoke(target, args); // 執行target的method方法 } public Class<?> getType() { return type; } }
MethodInvoker內部封裝了method.invoke來執行方法。
3 reflection包-此包中的類基本是加強類,提供對外開放的API
3.1 Reflector類-class類的加強類
public class Reflector { // 反射器,class的加強類 private static boolean classCacheEnabled = true; private static final String[] EMPTY_STRING_ARRAY = new String[0]; // 至關於緩存工廠,此處使用REFLECTOR_MAP目的是我的理解是由於Reflect的API很耗資源,因此用REFLECTOR_MAP將要反射的類及加強類放置在一塊兒,之後使用時能夠直接取不須要重複新建class的加強類了 private static final Map<Class<?>, Reflector> REFLECTOR_MAP = new ConcurrentHashMap<Class<?>, Reflector>(); private Class<?> type; // 該類的類信息 private String[] readablePropertyNames = EMPTY_STRING_ARRAY; // 可讀屬性 private String[] writeablePropertyNames = EMPTY_STRING_ARRAY; // 可寫屬性 private Map<String, Invoker> setMethods = new HashMap<String, Invoker>(); // set方法 private Map<String, Invoker> getMethods = new HashMap<String, Invoker>(); // get方法 private Map<String, Class<?>> setTypes = new HashMap<String, Class<?>>(); // setxxx中xxx類型 private Map<String, Class<?>> getTypes = new HashMap<String, Class<?>>(); // getxxx中的xxx類型 private Constructor<?> defaultConstructor; // 該類的默認構造函數 private Map<String, String> caseInsensitivePropertyMap = new HashMap<String, String>(); ...... }
查看Reflector的基本屬性,主要對一個類按照反射包括的數據結構(方法,屬性,構造方法)進行解析。如User類中有age,name屬性,且有get,set方法,那木在初始化時會將這些屬性和屬性,方法等解析出來。注意此類有一個靜態列表,用於存放已解析好的類。用於當作緩存使用。查看構造方法驗證。
private Reflector(Class<?> clazz) { // 構造函數初始化元數據信息 type = clazz; addDefaultConstructor(clazz); // 添加構造函數 addGetMethods(clazz); // 添加類的get方法 addSetMethods(clazz); // 添加類的set方法 addFields(clazz); // 添加屬性 readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]); // 獲取getxxx中xxx集合 writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]); // 獲取setxxx中xxx集合 for (String propName : readablePropertyNames) { caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName); } for (String propName : writeablePropertyNames) { caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName); } }
分析較簡單的添加構造函數方法,後面的添加get,set方法較爲複雜,限於篇幅,就略過了。
private void addDefaultConstructor(Class<?> clazz) { // 添加反射類元數據的默認構造函數 Constructor<?>[] consts = clazz.getDeclaredConstructors(); // 獲取全部構造函數 for (Constructor<?> constructor : consts) { if (constructor.getParameterTypes().length == 0) { // 找到無參構造函數,即默認構造函數 if (canAccessPrivateMethods()) { // 如果private,則變爲可寫 try { constructor.setAccessible(true); } catch (Exception e) { // Ignored. This is only a final precaution, nothing we can do. } } if (constructor.isAccessible()) { this.defaultConstructor = constructor; // 設置默認構造函數 } } } }
3.2 MetaClass類-Reflector類的加強類
該類主要對於複雜的語句進行拆解。若是需分析一個xxx字段在某個類中是否存在set方法,
對於通常的如age單屬性,能夠直接調用Reflector.hasSetter("age")來判斷。但若是對於group.user.age這個多屬性,需分析group中是否有user的set方法,若是有,則繼續分析在user對象中是否存在age的set方法。該實現主要是基於上文分析的PropertyTokenizer類與Reflector類。下文分析MetaClass中重寫的hasSetter方法來驗證。
// 判斷name是否在對象中存在set方法 public boolean hasSetter(String name) { PropertyTokenizer prop = new PropertyTokenizer(name); // 對複雜語句進行拆解 if (prop.hasNext()) { // 如果複雜語句 if (reflector.hasSetter(prop.getName())) { // 第一層解析的對象屬性有get方法 MetaClass metaProp = metaClassForProperty(prop.getName()); // 獲取getxxx中xxx的類型,構建成一個MetaClass對象,方便遞歸 return metaProp.hasSetter(prop.getChildren()); // 遞歸操做 } else { // 若有一層沒有get方法,就直接返回false return false; } } else { return reflector.hasSetter(prop.getName()); // 簡單語句直接調用reflector的方法 } }
3.3 MetaObject類-對外提供的類
MetaObject類裏面存放了真正的對象。前文所分析的都是些靜態對象,沒有真正涉及到實例對象。分析MetaObject對外接口,其實都是內部調用了ObjectWrapper的方法。分析ObjectWrapper很簡單,此處結合一個簡單demo,來理解MetaObject的做用。
class User{ private String name; private String age; // ..... 省略getName,setName,getAge,setAge方法 } @Test public void shouldGetAndSetField() { User user = new User(); MetaObject meta = SystemMetaObject.forObject(user); // 利用實例對象構建一個MetaObject對象 meta.setValue("name", "xiabing"); // 調用metaObject.setValue方法,實際調用的是objectWrapper方法 assertEquals("xiabing", meta.getValue("name")); // 比較,獲取name的屬性 }
MetaObject是對外提供api的類。要了解具體的實現,還需繼續分析下文的wrapper包。
4. wrapper包-對象裝飾包
如下爲我的理解:使用wrapper包來封裝對象,對外開放統一的set,get方法。好比咱們使用一個User對象,要設置名稱則需調用user.setName("haha")方法,設置年齡需調用user.setAge("10")。這樣對於mybatis可能不太友好,因而使用了wrapper類。只需wrapper.set("name","haha"),wrapper.set("age","10")這樣統一的接口方式就能設置屬性了。看起來確實簡潔許多。
4.1 ObjectWrapper接口,提供基本對外開放的接口
public interface ObjectWrapper { //對象裝飾類 Object get(PropertyTokenizer prop); // 得到屬性 void set(PropertyTokenizer prop, Object value); // 設置屬性 String findProperty(String name, boolean useCamelCaseMapping); //查找屬性 String[] getGetterNames(); // 獲取getXXX中xxx的集合 String[] getSetterNames(); // 獲取setXXX中的xxx的集合 Class<?> getSetterType(String name); // 根據xxx獲取setxxx中xxx的類型 Class<?> getGetterType(String name); // 根據xxx獲取getxxx中xxx的類型 boolean hasSetter(String name); // 查找是否存在setxxx方法 boolean hasGetter(String name); // 查找是否存在getxxx方法 MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory); // 實例化屬性的值 boolean isCollection(); // 該對象是不是集合 public void add(Object element); public <E> void addAll(List<E> element); }
4.2 BaseWrapper抽象類-提供集合屬性的方法
此類教簡單,暫且忽略掉。
4.3 BeanWrapper - 真實執行方法的類
此類是反射的關鍵,是真正調用反射執行get,set,method方法的類。分析其基本屬性,注意繼承了BaseWrapper,而BaseWrapper中也有一個屬性是MetaObject,當時說了MetaObject真正調用方法的是BaseWrapper類,可見,兩個對象是一一對應關係。
public class BeanWrapper extends BaseWrapper { // bean的封裝類 private Object object; // 真實對象 private MetaClass metaClass; // 該對象的反射類的加強類
分析get屬性方法方法
public Object get(PropertyTokenizer prop) { if (prop.getIndex() != null) { // 分析PropertyTokenizer可知,若index不爲null,則表明是集合對象,限於篇幅,小夥伴可自行分析 Object collection = resolveCollection(prop, object); return getCollectionValue(prop, collection); } else { return getBeanProperty(prop, object); // 調用內部方法 } } private Object getBeanProperty(PropertyTokenizer prop, Object object) { try { Invoker method = metaClass.getGetInvoker(prop.getName()); // 根據屬性拿到getFiledInvoker執行器 try { return method.invoke(object, NO_ARGUMENTS); // 調用getFiledInvoker中的方法 } 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); } }
上面用到了Invoke的方法,Invoke分析見上文。可知面向接口編程的優越性,將getFiled,setFiled,method全都封裝成了invoke類。
分析set屬性方法
public void set(PropertyTokenizer prop, Object value) { if (prop.getIndex() != null) { // 分析PropertyTokenizer可知,若index不爲null,則表明是集合對象,限 Object collection = resolveCollection(prop, object); setCollectionValue(prop, collection, value); } else { setBeanProperty(prop, object, value); //調用內部方法 } } private void setBeanProperty(PropertyTokenizer prop, Object object, Object value) { try { Invoker method = metaClass.getSetInvoker(prop.getName()); // 拿到setFiledInvoker執行器 Object[] params = {value}; // 獲取參數 try { method.invoke(object, params); // 執行setFiledInvoker方法,實際調用Filed.set()方法設置屬性 } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } catch (Throwable t) { throw new ReflectionException("Could not set property '" + prop.getName() + "' of '" + object.getClass() + "' with value '" + value + "' Cause: " + t.toString(), t); } }
總結
因爲之前對反射基礎不夠紮實,致使在分析反射包的時候,過程很不順利,不過還好堅持下來了。在過程當中,發現mybatis的功能架構清晰明瞭,給了我之後編程的靈感。若是解釋有誤的還請歡迎評論。任重而道遠,若是以爲不錯,還請看官點個小讚了。