該系列文檔是本人在學習 Mybatis 的源碼過程當中總結下來的,可能對讀者不太友好,請結合個人源碼註釋(Mybatis源碼分析 GitHub 地址、Mybatis-Spring 源碼分析 GitHub 地址、Spring-Boot-Starter 源碼分析 GitHub 地址)進行閱讀html
MyBatis 版本:3.5.2java
MyBatis-Spring 版本:2.0.3node
MyBatis-Spring-Boot-Starter 版本:2.1.4git
在《精盡 MyBatis 源碼分析 - 總體架構》中對 MyBatis 的基礎支持層已作過介紹,包含整個 MyBatis 的基礎模塊,爲核心處理層的功能提供了良好的支撐,本文對基礎支持層的每一個模塊進行分析github
主要包路徑:org.apache.ibatis.parsingspring
主要功能:初始化時解析mybatis-config.xml配置文件、爲處理動態SQL語句中佔位符提供支持sql
主要查看如下幾個類:數據庫
org.apache.ibatis.parsing.XPathParser
:基於Java XPath 解析器,用於解析MyBatis的mybatis-config.xml和**Mapper.xml等XML配置文件express
org.apache.ibatis.parsing.GenericTokenParser
:通用的Token解析器apache
org.apache.ibatis.parsing.PropertyParser
:動態屬性解析器
org.apache.ibatis.parsing.XPathParser
:基於Java XPath 解析器,用於解析MyBatis的mybatis-config.xml和**Mapper.xml等XML配置文件
主要代碼以下:
public class XPathParser { /** * XML Document 對象 */ private final Document document; /** * 是否檢驗 */ private boolean validation; /** * XML實體解析器 */ private EntityResolver entityResolver; /** * 變量對象 */ private Properties variables; /** * Java XPath 對象 */ private XPath xpath; public XPathParser(String xml) { commonConstructor(false, null, null); this.document = createDocument(new InputSource(new StringReader(xml))); } public String evalString(String expression) { return evalString(document, expression); } public String evalString(Object root, String expression) { // <1> 得到值 String result = (String) evaluate(expression, root, XPathConstants.STRING); // <2> 基於 variables 替換動態值,若是 result 爲動態值 result = PropertyParser.parse(result, variables); return result; } private Object evaluate(String expression, Object root, QName returnType) { try { // 經過XPath結合表達式獲取Document對象中的結果 return xpath.evaluate(expression, root, returnType); } catch (Exception e) { throw new BuilderException("Error evaluating XPath. Cause: " + e, e); } } public XNode evalNode(String expression) { return evalNode(document, expression); } public XNode evalNode(Object root, String expression) { // <1> 得到 Node 對象 Node node = (Node) evaluate(expression, root, XPathConstants.NODE); if (node == null) { return null; } // <2> 封裝成 XNode 對象 return new XNode(this, node, variables); } private Document createDocument(InputSource inputSource) { // important: this must only be called AFTER common constructor try { // 1> 建立 DocumentBuilderFactory 對象 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); factory.setValidating(validation); factory.setNamespaceAware(false); factory.setIgnoringComments(true); factory.setIgnoringElementContentWhitespace(false); factory.setCoalescing(false); factory.setExpandEntityReferences(true); // 2> 建立 DocumentBuilder 對象 DocumentBuilder builder = factory.newDocumentBuilder(); builder.setEntityResolver(entityResolver); // 設置實體解析器 builder.setErrorHandler(new ErrorHandler() { // 設置異常處理,實現都空的 @Override public void error(SAXParseException exception) throws SAXException { throw exception; } @Override public void fatalError(SAXParseException exception) throws SAXException { throw exception; } @Override public void warning(SAXParseException exception) throws SAXException { // NOP } }); // 3> 解析 XML 文件,將文件加載到Document中 return builder.parse(inputSource); } catch (Exception e) { throw new BuilderException("Error creating document instance. Cause: " + e, e); } } private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) { this.validation = validation; this.entityResolver = entityResolver; this.variables = variables; XPathFactory factory = XPathFactory.newInstance(); this.xpath = factory.newXPath(); } }
看到定義的幾個屬性:
類型 | 屬性名 | 說明 |
---|---|---|
Document | document | XML文件被解析後生成對應的org.w3c.dom.Document 對象 |
boolean | validation | 是否校驗XML文件,通常狀況下爲true |
EntityResolver | entityResolver | org.xml.sax.EntityResolver 對象,XML實體解析器,通常經過自定義的org.apache.ibatis.builder.xml.XMLMapperEntityResolver 從本地獲取DTD文件解析 |
Properties | variables | 變量Properties對象,用來替換須要動態配置的屬性值,例如咱們在MyBatis的配置文件中使用變量將用戶名密碼放在另一個配置文件中,那麼這個配置會被解析到Properties對象用,用於替換XML文件中的動態值 |
XPath | xpath | javax.xml.xpath.XPath 對象,用於查詢XML中的節點和元素 |
構造函數有不少,基本都類似,內部都是調用commonConstructor
方法設置相關屬性和createDocument
方法爲該XML文件建立一個Document對象
提供了一系列的eval*
方法,用於獲取Document對象中的元素或者節點:
variables
調用PropertyParser
的parse
方法替換掉其中的動態值(若是存在),這就是MyBatis如何替換掉XML中的動態值實現的方式org.w3c.dom.Node
節點對象,將其封裝成本身定義的XNode
對象,方便主要爲了動態值的替換org.apache.ibatis.parsing.PropertyParser
:動態屬性解析器
主要代碼以下:
public class PropertyParser { public static String parse(String string, Properties variables) { // <2.1> 建立 VariableTokenHandler 對象 VariableTokenHandler handler = new VariableTokenHandler(variables); // <2.2> 建立 GenericTokenParser 對象 GenericTokenParser parser = new GenericTokenParser("${", "}", handler); // <2.3> 執行解析 return parser.parse(string); } }
parse
方法:建立VariableTokenHandler對象和GenericTokenParser對象,而後調用GenericTokenParser的parse方法替換其中的動態值
org.apache.ibatis.parsing.GenericTokenParser
:通用的Token解析器
定義了是三個屬性:
public class GenericTokenParser { /** * 開始的 Token 字符串 */ private final String openToken; /** * 結束的 Token 字符串 */ private final String closeToken; /** * Token處理器 */ private final TokenHandler handler; }
根據開始字符串和結束字符串解析出裏面的表達式(例如${name}->name),而後經過TokenHandler進行解析處理
VariableTokenHandler
,是PropertyParser
的內部靜態類,變量Token處理器,根據Properties variables
變量對象將Token動態值解析成實際值
XPathParser
對象,其中會解析成對應的Document
對象,內部的Properties對象存儲動態變量的值PropertyParser
用於解析XML文件中的動態值,根據GenericTokenParser
獲取動態屬性的名稱(例如${name}->name),而後經過VariableTokenHandler
根據Properties對象獲取到動態屬性(name)對應的值主要功能:對Java原生的反射進行了良好的封裝,提供更加簡單易用的API,用於解析類對象
反射這一模塊的代碼寫得很漂亮,值得參考!!!
主要包路徑:org.apache.ibatis.reflection
以下所示:
主要查看如下幾個類:
org.apache.ibatis.reflection.Reflector
:保存Class類中定義的屬性相關信息並進行了簡單的映射org.apache.ibatis.reflection.invoker.MethodInvoker
:Class類中屬性對應set方法或者get方法的封裝org.apache.ibatis.reflection.DefaultReflectorFactory
:Reflector的工廠接口,用於建立和緩存Reflector對象org.apache.ibatis.reflection.MetaClass
:Class類的元數據,包裝Reflector,基於PropertyTokenizer(分詞器)提供對Class類的元數據一些操做,能夠理解成對Reflector操做的進一步加強org.apache.ibatis.reflection.DefaultObjectFactory
:實現了ObjectFactory工廠接口,用於建立Class類對象org.apache.ibatis.reflection.wrapper.BeanWrapper
:實現了ObjectWrapper對象包裝接口,繼承BaseWrapper抽象類,根據Object對象與其MetaClass元數據對象提供對該Object對象的一些操做org.apache.ibatis.reflection.MetaObject
:對象元數據,提供了操做對象的屬性等方法。 能夠理解成對ObjectWrapper操做的進一步加強org.apache.ibatis.reflection.SystemMetaObject
:用於建立MetaObject對象,提供了ObjectFactory、ObjectWrapperFactory、空MetaObject的單例org.apache.ibatis.reflection.ParamNameResolver
:方法參數名稱解析器,用於解析咱們定義的Mapper接口的方法org.apache.ibatis.reflection.Reflector
:保存Class類中定義的屬性相關信息並進行了簡單的映射
部分代碼以下:
public class Reflector { /** * Class類 */ private final Class<?> type; /** * 可讀屬性集合 */ private final String[] readablePropertyNames; /** * 可寫屬性集合 */ private final String[] writablePropertyNames; /** * 屬性對應的 setter 方法的映射。 * * key 爲屬性名稱 * value 爲 Invoker 對象 */ private final Map<String, Invoker> setMethods = new HashMap<>(); /** * 屬性對應的 getter 方法的映射。 * * key 爲屬性名稱 value 爲 Invoker 對象 */ private final Map<String, Invoker> getMethods = new HashMap<>(); /** * 屬性對應的 setter 方法的方法參數類型的映射。{@link #setMethods} * * key 爲屬性名稱 * value 爲方法參數類型 */ private final Map<String, Class<?>> setTypes = new HashMap<>(); /** * 屬性對應的 getter 方法的返回值類型的映射。{@link #getMethods} * * key 爲屬性名稱 * value 爲返回值的類型 */ private final Map<String, Class<?>> getTypes = new HashMap<>(); /** * 默認構造方法 */ private Constructor<?> defaultConstructor; /** * 全部屬性集合 * key 爲全大寫的屬性名稱 * value 爲屬性名稱 */ private Map<String, String> caseInsensitivePropertyMap = new HashMap<>(); public Reflector(Class<?> clazz) { // 設置對應的類 type = clazz; // <1> 初始化 defaultConstructor 默認構造器,也就是無參構造器 addDefaultConstructor(clazz); // <2> 初始化 getMethods 和 getTypes addGetMethods(clazz); // <3> 初始化 setMethods 和 setTypes addSetMethods(clazz); // <4> 可能有些屬性沒有get或者set方法,則直接將該Field字段封裝成SetFieldInvoker或者GetFieldInvoker,而後分別保存至上面4個變量中 addFields(clazz); // <5> 初始化 readablePropertyNames、writeablePropertyNames、caseInsensitivePropertyMap 屬性 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類,整個解析過程並不複雜,我這裏就不所有講述了,可閱讀相關代碼,已作好註釋😈😈😈
解析後保存了Class類的如下信息:
類型 | 字段 | 說明 |
---|---|---|
Class<?> | type | Class類 |
String[] | readablePropertyNames | 可讀屬性集合 |
String[] | writablePropertyNames | 可寫屬性集合 |
Map<String, Invoker> | setMethods | 屬性對應的 setter 方法的映射:<屬性名稱, MethodFieldInvoker對象> |
Map<String, Invoker> | getMethods | 屬性對應的 getter 方法的映射:<屬性名稱, MethodFieldInvoker對象> |
Map<String, Class<?>> | setTypes | 屬性對應的 setter 方法的方法參數類型的映射:<屬性名稱, 方法參數類型> |
Map<String, Class<?>> | getTypes | 屬性對應的 getter 方法的返回值類型的映射:<屬性名稱, 方法返回值類型> |
Constructor<?> | defaultConstructor | 默認構造方法 |
Map<String, String> | caseInsensitivePropertyMap | 全部屬性集合:<屬性名稱(全大寫), 屬性名稱> |
org.apache.ibatis.reflection.invoker.MethodInvoker
:Class類中屬性對應set方法或者get方法的封裝
代碼以下:
public class MethodInvoker implements Invoker { /** * 類型 */ private final Class<?> type; /** * 指定方法 */ private final Method method; public MethodInvoker(Method method) { this.method = method; if (method.getParameterTypes().length == 1) { // 參數大小爲 1 時,通常是 setter 方法,設置 type 爲方法參數[0] type = method.getParameterTypes()[0]; } else { // 不然,通常是 getter 方法,設置 type 爲返回類型 type = method.getReturnType(); } } @Override public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException { try { return method.invoke(target, args); } catch (IllegalAccessException e) { if (Reflector.canControlMemberAccessible()) { method.setAccessible(true); return method.invoke(target, args); } else { throw e; } } } @Override public Class<?> getType() { return type; } }
在其構造函數中,設置set方法或者get方法,並獲取其參數類型或者返回值類型進行保存,也就是該屬性的類型
若是Class類中有些屬性沒有set或者get方法,那麼這些屬性會被封裝成下面兩個對象(final static修飾的字段不會被封裝),用於設置或者獲取他們的值
org.apache.ibatis.reflection.invoker.SetFieldInvoker
、org.apache.ibatis.reflection.invoker.GetFieldInvoker
org.apache.ibatis.reflection.DefaultReflectorFactory
:Reflector的工廠接口,用於建立和緩存Reflector對象
代碼以下:
public class DefaultReflectorFactory implements ReflectorFactory { /** * 是否緩存 */ private boolean classCacheEnabled = false; /** * Reflector 的緩存映射 * * KEY:Class 對象 * VALUE: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; } @Override public Reflector findForClass(Class<?> type) { if (classCacheEnabled) { // synchronized (type) removed see issue #461 return reflectorMap.computeIfAbsent(type, Reflector::new); } else { return new Reflector(type); } } }
根據Class對象建立Reflector對象,代碼比較簡單
org.apache.ibatis.reflection.MetaClass
:Class類的元數據,包裝Reflector,基於PropertyTokenizer(分詞器)提供對Class類的元數據各類操做,能夠理解成對Reflector操做的進一步加強
其中包含了ReflectorFactory和Reflector兩個字段,經過PropertyTokenizer分詞器提供了獲取屬性的名稱和返回值類型等等方法,也就是在Reflector上面新增了一些方法
org.apache.ibatis.reflection.propertyPropertyTokenizer
分詞器用於解析相似於'map[qm].user'這樣的屬性,將其分隔保存方便獲取,可閱讀相關代碼哦😈😈😈
org.apache.ibatis.reflection.DefaultObjectFactory
:實現了ObjectFactory工廠接口,用於建立Class類對象
部分代碼以下:
public class DefaultObjectFactory implements ObjectFactory, Serializable { @Override public <T> T create(Class<T> type) { return create(type, null, null); } @SuppressWarnings("unchecked") @Override public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) { // 獲取須要建立的類 Class<?> classToCreate = resolveInterface(type); // 建立指定類的對象 return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs); } protected Class<?> resolveInterface(Class<?> type) { Class<?> classToCreate; if (type == List.class || type == Collection.class || type == Iterable.class) { classToCreate = ArrayList.class; } else if (type == Map.class) { classToCreate = HashMap.class; } else if (type == SortedSet.class) { // issue #510 Collections Support classToCreate = TreeSet.class; } else if (type == Set.class) { classToCreate = HashSet.class; } else { classToCreate = type; } return classToCreate; } private <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) { try { Constructor<T> constructor; if (constructorArgTypes == null || constructorArgs == null) { // 使用默認的構造器 constructor = type.getDeclaredConstructor(); try { // 返回實例 return constructor.newInstance(); } catch (IllegalAccessException e) { if (Reflector.canControlMemberAccessible()) { constructor.setAccessible(true); return constructor.newInstance(); } else { throw e; } } } // 經過參數類型列表獲取構造器 constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()])); try { // 返回實例 return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()])); } catch (IllegalAccessException e) { if (Reflector.canControlMemberAccessible()) { constructor.setAccessible(true); return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()])); } else { throw e; } } } catch (Exception e) { ... } } }
經過Class對象獲取構造函數,而後經過構造函數建立一個實例對象
org.apache.ibatis.reflection.wrapper.BeanWrapper
:實現了ObjectWrapper對象包裝接口,繼承BaseWrapper抽象類,根據Object對象與其MetaClass元數據對象提供對該Object對象的一些操做,部分代碼以下:
public class BeanWrapper extends BaseWrapper { /** * 普通對象 */ private final Object object; private final MetaClass metaClass; public BeanWrapper(MetaObject metaObject, Object object) { super(metaObject); this.object = object; // 建立 MetaClass 對象 this.metaClass = MetaClass.forClass(object.getClass(), metaObject.getReflectorFactory()); } @Override public Object get(PropertyTokenizer prop) { // <1> 得到集合類型的屬性的指定位置的值 if (prop.getIndex() != null) { // 得到集合類型的屬性 Object collection = resolveCollection(prop, object); // 得到指定位置的值 return getCollectionValue(prop, collection); // <2> 得到屬性的值 } else { return getBeanProperty(prop, object); } } @Override public void set(PropertyTokenizer prop, Object value) { // 設置集合類型的屬性的指定位置的值 if (prop.getIndex() != null) { // 得到集合類型的屬性 Object collection = resolveCollection(prop, object); // 設置指定位置的值 setCollectionValue(prop, collection, value); } else { // 設置屬性的值 setBeanProperty(prop, object, value); } } }
get
方法:根據分詞器從object
對象中獲取對應的屬性值
set
方法:根據分詞器往object
對象中設置屬性值
還包含了其餘不少操做object
對象的方法,這裏不所有羅列出來了,請閱讀其相關代碼進行查看😈😈😈
org.apache.ibatis.reflection.MetaObject
:對象元數據,提供了操做對象的屬性等方法,能夠理解成對 ObjectWrapper 操做的進一步加強
在Mybatis中若是須要操做某個對象(實例類或者集合),都會轉換成MetaObject類型,便於操做
主要代碼以下:
public class MetaObject { /** * 原始 Object 對象 */ private final Object originalObject; /** * 封裝過的 Object 對象 */ private final ObjectWrapper objectWrapper; private final ObjectFactory objectFactory; private final ObjectWrapperFactory objectWrapperFactory; private final ReflectorFactory reflectorFactory; 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) { this.objectWrapper = (ObjectWrapper) object; } else if (objectWrapperFactory.hasWrapperFor(object)) { this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object); } else if (object instanceof Map) { this.objectWrapper = new MapWrapper(this, (Map) object); } else if (object instanceof Collection) { this.objectWrapper = new CollectionWrapper(this, (Collection) object); } else { this.objectWrapper = new BeanWrapper(this, object); } } /** * 建立 MetaObject 對象 * * @param object 原始 Object 對象 * @param objectFactory 生產 Object 的實例工廠 * @param objectWrapperFactory 建立 ObjectWrapper 工廠,沒有默認實現,沒有用到 * @param reflectorFactory 建立 Object 對應 Reflector 的工廠 * @return MetaObject 對象 */ public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) { if (object == null) { return SystemMetaObject.NULL_META_OBJECT; } else { return new MetaObject(object, objectFactory, objectWrapperFactory, reflectorFactory); } } public String findProperty(String propName, boolean useCamelCaseMapping) { return objectWrapper.findProperty(propName, useCamelCaseMapping); } /** * 獲取指定屬性的值,遞歸處理 * * @param name 屬性名稱 * @return 屬性值 */ public Object getValue(String name) { // 建立 PropertyTokenizer 對象,對 name 分詞 PropertyTokenizer prop = new PropertyTokenizer(name); if (prop.hasNext()) { // 有子表達式 // 建立 MetaObject 對象 MetaObject metaValue = metaObjectForProperty(prop.getIndexedName()); // <2> 遞歸判斷子表達式 children ,獲取值 if (metaValue == SystemMetaObject.NULL_META_OBJECT) { return null; } else { return metaValue.getValue(prop.getChildren()); } } else { // 無子表達式 // <1> 獲取值 return objectWrapper.get(prop); } } /** * 設置指定屬性值 * * @param name 屬性名稱 * @param value 屬性值 */ public void setValue(String name, Object value) { PropertyTokenizer prop = new PropertyTokenizer(name); if (prop.hasNext()) { MetaObject metaValue = metaObjectForProperty(prop.getIndexedName()); if (metaValue == SystemMetaObject.NULL_META_OBJECT) { if (value == null) { // don't instantiate child path if value is null return; } else { // <1> 建立值 metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory); } } metaValue.setValue(prop.getChildren(), value); } else { // <1> 設置值 objectWrapper.set(prop, value); } } /** * 建立指定屬性的 MetaObject 對象 * * @param name 屬性名稱 * @return MetaObject 對象 */ public MetaObject metaObjectForProperty(String name) { // 得到屬性值 Object value = getValue(name); // 建立 MetaObject 對象 return MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory); } }
咱們能夠看到構造方法是私有的,沒法直接經過構造函數建立實例對象,提供了一個forObject
靜態方法來建立一個MetaObject對象:
若是原始Object對象爲null,則返回空的MetaObject對象NULL_META_OBJECT
,在SystemMetaObject
中定義的一個單例對象,實際就是將MetaObject內部的Object原始對象設置爲NullObject
(一個靜態類)
不然經過構造函數建立MetaObject對象,在它的構造函數中能夠看到,根據Object對象的類型來決定建立什麼類型的ObjectWrapper
,並無用到ObjectWrapperFactory
工廠接口(默認實現也拋出異常)
對於一個已經初始化好的MetaObject對象,能夠經過getValue
方法獲取指定屬性的值,setValue
設置指定屬性值
org.apache.ibatis.reflection.SystemMetaObject
:系統級別的MetaObject對象,提供了ObjectFactory、ObjectWrapperFactory、空MetaObject的單例
代碼以下:
public final class SystemMetaObject { /** * ObjectFactory 的單例 */ public static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory(); /** * ObjectWrapperFactory 的單例 */ public static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory(); /** * 空對象的 MetaObject 對象單例 */ public static final MetaObject NULL_META_OBJECT = MetaObject.forObject(NullObject.class, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory()); private SystemMetaObject() { // Prevent Instantiation of Static Class } private static class NullObject { } /** * 建立 MetaObject 對象 * * @param object 指定對象 * @return MetaObject 對象 */ public static MetaObject forObject(Object object) { return MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory()); } }
內部就定義了一個forObject(Object object)
靜態方法,用於建立MetaObject對象
咱們通常會將Entity實體類或者Map集合解析成MetaObject對象,而後能夠對其屬性進行操做
org.apache.ibatis.reflection.ParamNameResolver
:方法參數名稱解析器,用於解析咱們定義的Mapper接口的方法
在org.apache.ibatis.binding.MapperMethod
的MethodSignature
內部類會用到
主要代碼以下:
public class ParamNameResolver { private static final String GENERIC_NAME_PREFIX = "param"; /** * 參數名映射 * KEY:參數順序 * VALUE:參數名 */ private final SortedMap<Integer, String> names; /** * 是否有 {@link Param} 註解的參數 */ private boolean hasParamAnnotation; public ParamNameResolver(Configuration config, Method method) { // 獲取方法的參數類型集合 final Class<?>[] paramTypes = method.getParameterTypes(); // 獲取方法的參數上面的註解集合 final Annotation[][] paramAnnotations = method.getParameterAnnotations(); final SortedMap<Integer, String> map = new TreeMap<>(); int paramCount = paramAnnotations.length; // get names from @Param annotations for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) { // 忽略 RowBounds、ResultHandler參數類型 if (isSpecialParameter(paramTypes[paramIndex])) { // skip special parameters continue; } String name = null; // <1> 首先,從 @Param 註解中獲取參數名 for (Annotation annotation : paramAnnotations[paramIndex]) { if (annotation instanceof Param) { hasParamAnnotation = true; name = ((Param) annotation).value(); break; } } if (name == null) { // @Param was not specified. // <2> 其次,獲取真實的參數名 if (config.isUseActualParamName()) { // 默認開啓 name = getActualParamName(method, paramIndex); } // <3> 最差,使用 map 的順序,做爲編號 if (name == null) { // use the parameter index as the name ("0", "1", ...) // gcode issue #71 name = String.valueOf(map.size()); } } // 添加到 map 中 map.put(paramIndex, name); } // 構建不可變的 SortedMap 集合 names = Collections.unmodifiableSortedMap(map); } /** * 根據參數值返回參數名稱與參數值的映射關係 * * @param args 參數值數組 * @return 參數名稱與參數值的映射關係 */ public Object getNamedParams(Object[] args) { final int paramCount = names.size(); // 無參數,則返回 null if (args == null || paramCount == 0) { return null; // 只有1個參數,而且沒有 @Param 註解,則直接返回該值 } else if (!hasParamAnnotation && paramCount == 1) { return args[names.firstKey()]; } else { /* * 參數名稱與值的映射,包含如下兩種組合數據: * 組合1:(參數名,值) * 組合2:(param+參數順序,值) */ final Map<String, Object> param = new ParamMap<>(); int i = 0; for (Map.Entry<Integer, String> entry : names.entrySet()) { // 組合 1 :添加到 param 中 param.put(entry.getValue(), args[entry.getKey()]); // add generic param names (param1, param2, ...) final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1); // ensure not to overwrite parameter named with @Param if (!names.containsValue(genericParamName)) { // 組合 2 :添加到 param 中 param.put(genericParamName, args[entry.getKey()]); } i++; } return param; } } }
在構造函數中能夠看到,目的是獲取到該方法的參數名,將參數順序與參數名進行映射保存在UnmodifiableSortedMap
一個不可變的SortedMap集合中,大體邏輯:
若是添加了@Param
註解,則參數名稱爲該註解的value值
沒有添加@Param註解則嘗試獲取真實的參數名
說明:經過反射獲取方法的參數名,咱們只能獲取到 arg0,arg1 的名稱,由於jdk8以後這些變量名稱沒有被編譯到class文件中,編譯時須要指定
-parameters
選項,方法的參數名纔會記錄到class文件中,運行時咱們就能夠經過反射機制獲取到
仍是沒有獲取到參數名則使用序號標記,通常不會走到這一步
還有一個getNamedParams
方法,根據實際入參數組返回參數名與參數值的映射,大體邏輯:
實際參數爲null或者參數個數爲0,則直接返回null
沒有使用@Param
註解而且參數個數爲1,則直接返回參數值
根據參數順序與參數名的映射獲取到參數名與參數值的映射
,並且還會將(param+參數順序)與參數值進行映射
,最後將兩種組合的映射返回
- 經過
Configuration
全局配置對象的newMetaObject(Object object)
方法建立,會傳入DefaultObjectFactory
、DefaultObjectWrapperFactory
和DefaultReflectorFactory
幾個默認實現類- 內部調用
MetaObject
的forObject
靜態方法,經過它的構造方法建立一個實例對象- 在MetaObject的構造函數中,會根據Object對象的類型來建立
ObjectWrapper
對象- 若是是建立
BeanWrapper
,則在其構造函數中,會再調用MetaClass的forClass方法建立MetaClass
對象,也就是經過其構造函數建立一個實例對象- 若是是
MapWrapper
,則直接複製給內部的Map<String, Object> map
屬性便可,其餘集合對象相似- 在MetaClass的構造函數中,會經過調用
DefaultReflectorFactory
的findForClass方法建立Reflector
對象- 在Reflector的構造函數中,經過反射機制解析該Class類,屬性的set和get方法會被封裝成
MethodInvoker
對象
MyBatis的幾個基本的Exception異常類在org.apache.ibatis.exceptions包路徑下
org.apache.ibatis.exceptions.IbatisException
:實現 RuntimeException 類,MyBatis 的異常基類
org.apache.ibatis.exceptions.PersistenceException
:繼承 IbatisException 類,目前 MyBatis 真正的異常基類
org.apache.ibatis.exceptions.ExceptionFactory
:異常工廠
每一個模塊都有本身都有的異常類,代碼都是相同的,這裏就不一一展現了
MyBatis支持三種數據源配置,分別爲UNPOOLED
、POOLED
和JNDI
。內部提供了兩種數據源實現,分別是UnpooledDataSource
和PooledDataSource
。在三種數據源配置中,UNPOOLED 和 POOLED 是經常使用的兩種配置。至於 JNDI,MyBatis 提供這種數據源的目的是爲了讓其可以運行在 EJB 或應用服務器等容器中,這一點官方文檔中有所說明。因爲 JNDI 數據源在平常開發中使用甚少,所以,本篇文章不打算分析 JNDI 數據源相關實現。你們如有興趣,可自行分析。
實際場景下,咱們基本不用 MyBatis 自帶的數據庫鏈接池的實現,這裏是讓咱們是對數據庫鏈接池的實現有個大致的理解。
主要包路徑:org.apache.ibatis.datasource
主要功能:數據源的實現,與MySQL鏈接的管理
主要查看如下幾個類:
org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory
:實現 DataSourceFactory 接口,非池化的 DataSourceFactory 實現類org.apache.ibatis.datasource.unpooled.UnpooledDataSource
:實現 DataSource 接口,非池化的 DataSource 對象org.apache.ibatis.datasource.pooled.PooledDataSourceFactory
:繼承 UnpooledDataSourceFactory 類,池化的 DataSourceFactory 實現類org.apache.ibatis.datasource.pooled.PooledDataSource
:實現 DataSource 接口,池化的 DataSource 實現類org.apache.ibatis.datasource.pooled.PoolState
:鏈接池狀態,記錄空閒和激活的 PooledConnection 集合,以及相關的數據統計org.apache.ibatis.datasource.pooled.PooledConnection
:實現 InvocationHandler 接口,池化的 Connection 對象org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory
:實現 DataSourceFactory 接口,非池化的 DataSourceFactory 實現類
代碼以下:
public class UnpooledDataSourceFactory implements DataSourceFactory { private static final String DRIVER_PROPERTY_PREFIX = "driver."; private static final int DRIVER_PROPERTY_PREFIX_LENGTH = DRIVER_PROPERTY_PREFIX.length(); protected DataSource dataSource; public UnpooledDataSourceFactory() { this.dataSource = new UnpooledDataSource(); } @Override public void setProperties(Properties properties) { Properties driverProperties = new Properties(); // 建立 dataSource 對應的 MetaObject 對象,其中是BeanWrapper MetaObject metaDataSource = SystemMetaObject.forObject(dataSource); // 遍歷 properties 屬性,初始化到 driverProperties 和 MetaObject 中 for (Object key : properties.keySet()) { String propertyName = (String) key; // 初始化到 driverProperties 中 if (propertyName.startsWith(DRIVER_PROPERTY_PREFIX)) { String value = properties.getProperty(propertyName); driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value); // 若是該屬性在UnpooledDataSource中有setter方法,則初始化到 MetaObject 中 } else if (metaDataSource.hasSetter(propertyName)) { String value = (String) properties.get(propertyName); Object convertedValue = convertValue(metaDataSource, propertyName, value); // 將數據設置到UnpooledDataSource中去 metaDataSource.setValue(propertyName, convertedValue); } else { throw new DataSourceException("Unknown DataSource property: " + propertyName); } } if (driverProperties.size() > 0) { // 設置 driverProperties 到 MetaObject 中 metaDataSource.setValue("driverProperties", driverProperties); } } @Override public DataSource getDataSource() { return dataSource; } private Object convertValue(MetaObject metaDataSource, String propertyName, String value) { Object convertedValue = value; // 得到該屬性的 setting 方法的參數類型 Class<?> targetType = metaDataSource.getSetterType(propertyName); // 轉化 if (targetType == Integer.class || targetType == int.class) { convertedValue = Integer.valueOf(value); } else if (targetType == Long.class || targetType == long.class) { convertedValue = Long.valueOf(value); } else if (targetType == Boolean.class || targetType == boolean.class) { convertedValue = Boolean.valueOf(value); } return convertedValue; } }
setProperties(Properties properties)
方法:
該dataSource
建立對應的MetaObject
對象,便於設置相應的屬性
將入參Properties
對象中的配置往dataSource
設置
若是是以driver.
開頭的配置則統一放入一個Properties中,而後設置到dataSource
的driverProperties
屬性中
getDataSource()
方法:直接返回建立好的數據源dataSource
org.apache.ibatis.datasource.unpooled.UnpooledDataSource
:實現 DataSource 接口,非池化的 DataSource 對象
獲取數據庫鏈接的方法:
public class UnpooledDataSource implements DataSource { private Connection doGetConnection(String username, String password) throws SQLException { // 建立 Properties 對象 Properties props = new Properties(); // 設置 driverProperties 到 props 中 if (driverProperties != null) { props.putAll(driverProperties); } // 設置 user 和 password 到 props 中 if (username != null) { props.setProperty("user", username); } if (password != null) { props.setProperty("password", password); } return doGetConnection(props); } private Connection doGetConnection(Properties properties) throws SQLException { // <1> 初始化 Driver initializeDriver(); // <2> 得到 Connection 對象 Connection connection = DriverManager.getConnection(url, properties); // <3> 配置 Connection 對象 configureConnection(connection); return connection; } private synchronized void initializeDriver() throws SQLException { // 判斷 registeredDrivers 是否已經存在該 driver ,若不存在,進行初始化 if (!registeredDrivers.containsKey(driver)) { Class<?> driverType; try { // <2> 得到 driver 類 if (driverClassLoader != null) { driverType = Class.forName(driver, true, driverClassLoader); } else { driverType = Resources.classForName(driver); } // DriverManager requires the driver to be loaded via the system ClassLoader. // http://www.kfu.com/~nsayer/Java/dyn-jdbc.html // <3> 建立 Driver 對象 Driver driverInstance = (Driver) driverType.getDeclaredConstructor().newInstance(); // 建立 DriverProxy 對象(爲了使用本身定義的Logger對象),並註冊到 DriverManager 中 DriverManager.registerDriver(new DriverProxy(driverInstance)); // 添加到 registeredDrivers 中 registeredDrivers.put(driver, driverInstance); } catch (Exception e) { throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e); } } } private void configureConnection(Connection conn) throws SQLException { if (defaultNetworkTimeout != null) { conn.setNetworkTimeout(Executors.newSingleThreadExecutor(), defaultNetworkTimeout); } if (autoCommit != null && autoCommit != conn.getAutoCommit()) { conn.setAutoCommit(autoCommit); } if (defaultTransactionIsolationLevel != null) { conn.setTransactionIsolation(defaultTransactionIsolationLevel); } } }
doGetConnection(Properties properties)
方法:
該類還包含了數據庫鏈接的基本信息以及其餘方法,我這裏沒有所有列出來,感興趣的能夠看下這個類😈
org.apache.ibatis.datasource.pooled.PooledDataSourceFactory
:繼承 UnpooledDataSourceFactory 類,池化的 DataSourceFactory 實現類
和 UnpooledDataSourceFactory 的區別是它建立了 PooledDataSource 對象
org.apache.ibatis.datasource.pooled.PooledDataSource
:實現 DataSource 接口,池化的 DataSource 實現類,包含數據庫的鏈接信息以及鏈接池的配置信息
下面的方法都有點長,我就】就不列出來了,能夠根據流程圖並結合註釋進行查看😈
getConnection()
方法:用於獲取鏈接,流程圖:
pushConnection
方法:"關閉"一個鏈接,放入空閒鏈接隊列中或者關設置爲無效狀態並關閉真正的鏈接,在PooledConnection
的invoke
方法中,若是方法名稱爲close
,表示須要關閉該鏈接,則調用該方法,流程圖:
pingConnection
方法:經過向數據庫發起 poolPingQuery
語句來發起"ping"操做,以判斷數據庫鏈接是否有效,在PooledConnection
的isValid
方法中被調用,PooledDataSource的獲取鏈接和關閉鏈接時都會判斷該鏈接是否有效
forceCloseAll
方法:用於關閉全部的鏈接,釋放資源,在定義的finalize()
方法中會被調用,即當前PooledDataSource對象被釋放時
org.apache.ibatis.datasource.pooled.PoolState
:鏈接池狀態,記錄空閒和激活的PooledConnection
集合,以及相關的數據統計
至關於一個池子,保存了空閒的或者正在被使用的鏈接以及一些鏈接池的配置信息
org.apache.ibatis.datasource.pooled.PooledConnection
:實現InvocationHandler
接口,池化的 Connection 對象
鏈接池中保存的都是封裝成PooledConnection的鏈接,使用JDK動態代理
的方式,調用PooledConnection的全部方法都會委託給invoke
方法:
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); // <1> 判斷是否爲 CLOSE 方法,則將鏈接放回到鏈接池中,避免鏈接被關閉 if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) { dataSource.pushConnection(this); return null; } try { // <2.1> 判斷非 Object 的方法,則先檢查鏈接是否可用 if (!Object.class.equals(method.getDeclaringClass())) { // issue #579 toString() should never fail // throw an SQLException instead of a Runtime checkConnection(); } // <2.2> 反射調用對應的方法 return method.invoke(realConnection, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } }
由於咱們將數據庫鏈接Connection封裝成了PooledConnection,代理的時候進行了如下操做:
調用Connection的close方法時,有時候不用真正的關閉,須要進行一些處理
調用Connection其餘方法以前,須要先檢測該鏈接的可用性,而後再執行該方法
實際咱們不會用到Mybatis內部的數據源,都是經過Druid
或者HikariCP
等第三方組件,這裏咱們來看看PooledConnection使用的JDK動態代理
PooledConnection實現了InvocationHandler
接口,在invoke方法中進行了預處理,例如檢查鏈接是否有效,而後在經過真實的Connection操做
在它的構造方法中初始化了代理類實例:
this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);
經過java.lang.reflect.Proxy
的newProxyInstance
方法實例化代理類對象,入參分別是類加載器,代理的接口,調用處理程序
在使用PooledConnection時,實際上MyBatis是使用內部的proxyConnection代理類的實例對象
主要包路徑:org.apache.ibatis.transaction
MyBatis的JdbcTransaction事務和純粹的JDBC事務幾乎沒什麼差異,僅是拓展了支持鏈接池的鏈接,事務管理也支持簡單的實現,代碼都比較簡單,這裏不展開討論
在咱們結合Spring一塊兒使用MyBatis的時候,通常使用Spring的事務與事務管理
主要包路徑:org.apache.ibatis.cache
MyBatis 提供了一級緩存和二級緩存,這兩種緩存都依賴於該緩存模塊,提供不少種的類型的緩存容器,使用了常見的裝飾者模式
提供的這兩種緩存性能很差且存在缺陷,很雞肋,通常不使用,若是須要使用緩存可閱讀個人另外一篇文檔:JetCache源碼分析
org.apache.ibatis.cache.Cache
:緩存容器接口,相似於HashMap,存放緩存數據。
有如下緩存容器的實現:
org.apache.ibatis.cache.impl.PerpetualCache
:基於HashMap,永不過時
org.apache.ibatis.cache.decorators.LoggingCache
:裝飾Cache,提供日誌打印與緩存命中統計
org.apache.ibatis.cache.decorators.BlockingCache
:裝飾Cache,阻塞實現,防止重複添加
org.apache.ibatis.cache.decorators.SynchronizedCache
:裝飾Cache,同步實現,添加synchronized
修飾符
org.apache.ibatis.cache.decorators.SerializedCache
:裝飾Cache,支持序列化緩存值
org.apache.ibatis.cache.decorators.ScheduledCache
:裝飾Cache,定時清空整個容器
org.apache.ibatis.cache.decorators.FifoCache
:裝飾Cache,能夠設置容量,採用先進先出的淘汰機制
org.apache.ibatis.cache.decorators.LruCache
:裝飾Cache,容量爲1024,基於 LinkedHashMap 實現淘汰機制
org.apache.ibatis.cache.decorators.WeakCache
:裝飾Cache,使用緩存值的強引用
org.apache.ibatis.cache.decorators.SoftCache
:裝飾Cache,使用緩存值的軟引用
另外也定義了緩存鍵org.apache.ibatis.cache.CacheKey
,能夠理解成將多個對象放在一塊兒,計算緩存鍵
二級緩存和Executor執行器有很大關聯,在後面的文檔中進行解析
主要包路徑:org.apache.ibatis.type
主要功能:
這個模塊涉及到不少類,由於不一樣類型須要有對應的類型處理器,邏輯都差很少
主要查看如下幾個類:
org.apache.ibatis.type.TypeHandler
:類型處理器接口
org.apache.ibatis.type.BaseTypeHandler
:實現 TypeHandler 接口,繼承 TypeReference 抽象類,TypeHandler 基礎抽象類
org.apache.ibatis.type.IntegerTypeHandler
:繼承 BaseTypeHandler 抽象類,Integer 類型的 TypeHandler 實現類
org.apache.ibatis.type.UnknownTypeHandler
:繼承 BaseTypeHandler 抽象類,未知的 TypeHandler 實現類,經過獲取對應的 TypeHandler ,進行處理
org.apache.ibatis.type.TypeHandlerRegistry
:TypeHandler 註冊表,至關於管理 TypeHandler 的容器,從其中能獲取到對應的 TypeHandler
org.apache.ibatis.type.TypeAliasRegistry
:類型與別名的註冊表,經過別名咱們能夠在 XML 映射文件中配置 resultType
和 parameterType
屬性時,直接配置爲別名而不用寫全類名
org.apache.ibatis.type.TypeHandler
:類型處理器接口,代碼以下:
public interface TypeHandler<T> { /** * 設置 PreparedStatement 的指定參數 * * Java Type => JDBC Type * * @param ps PreparedStatement 對象 * @param i 參數佔位符的位置 * @param parameter 參數 * @param jdbcType JDBC 類型 * @throws SQLException 當發生 SQL 異常時 */ void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException; /** * 得到 ResultSet 的指定字段的值 * * JDBC Type => Java Type * * @param rs ResultSet 對象 * @param columnName 字段名 * @return 值 * @throws SQLException 當發生 SQL 異常時 */ T getResult(ResultSet rs, String columnName) throws SQLException; /** * 得到 ResultSet 的指定字段的值 * * JDBC Type => Java Type * * @param rs ResultSet 對象 * @param columnIndex 字段位置 * @return 值 * @throws SQLException 當發生 SQL 異常時 */ T getResult(ResultSet rs, int columnIndex) throws SQLException; /** * 得到 CallableStatement 的指定字段的值 * * JDBC Type => Java Type * * @param cs CallableStatement 對象,支持調用存儲過程 * @param columnIndex 字段位置 * @return 值 * @throws SQLException */ T getResult(CallableStatement cs, int columnIndex) throws SQLException; }
#setParameter(...)
方法,是 Java Type => Jdbc Type 的過程
#getResult(...)
方法,是 Jdbc Type => Java Type 的過程
org.apache.ibatis.type.BaseTypeHandler
:實現 TypeHandler 接口,繼承 TypeReference 抽象類,TypeHandler 基礎抽象類
在方法的實現中捕獲異常,內部調用抽象方,交由子類去實現
org.apache.ibatis.type.IntegerTypeHandler
:繼承 BaseTypeHandler 抽象類,Integer 類型的 TypeHandler 實現類
往java.sql.PreparedStatement
設置參數或者從java.sql.ResultSet
獲取結果,邏輯不復雜,代碼以下:
public class IntegerTypeHandler extends BaseTypeHandler<Integer> { @Override public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType) throws SQLException { // 直接設置參數便可 ps.setInt(i, parameter); } @Override public Integer getNullableResult(ResultSet rs, String columnName) throws SQLException { // 得到字段的值 int result = rs.getInt(columnName); // 先經過 rs 判斷是否空,若是是空,則返回 null ,不然返回 result return result == 0 && rs.wasNull() ? null : result; } @Override public Integer getNullableResult(ResultSet rs, int columnIndex) throws SQLException { int result = rs.getInt(columnIndex); return result == 0 && rs.wasNull() ? null : result; } @Override public Integer getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { int result = cs.getInt(columnIndex); return result == 0 && cs.wasNull() ? null : result; } }
org.apache.ibatis.type.UnknownTypeHandler
:繼承 BaseTypeHandler 抽象類,未知的 TypeHandler 實現類,經過獲取對應的 TypeHandler ,進行處理
內部有一個TypeHandlerRegistry
對象,TypeHandler 註冊表,保存了Java Type、JDBC Type與TypeHandler 之間的映射關係
代碼以下:
public class UnknownTypeHandler extends BaseTypeHandler<Object> { /** * ObjectTypeHandler 單例 */ private static final ObjectTypeHandler OBJECT_TYPE_HANDLER = new ObjectTypeHandler(); /** * TypeHandler 註冊表 */ private TypeHandlerRegistry typeHandlerRegistry; private TypeHandler<?> resolveTypeHandler(Object parameter, JdbcType jdbcType) { TypeHandler<?> handler; // 參數爲空,返回 OBJECT_TYPE_HANDLER if (parameter == null) { handler = OBJECT_TYPE_HANDLER; } else { // 參數非空,使用參數類型得到對應的 TypeHandler handler = typeHandlerRegistry.getTypeHandler(parameter.getClass(), jdbcType); // check if handler is null (issue #270) // 獲取不到,則使用 OBJECT_TYPE_HANDLER if (handler == null || handler instanceof UnknownTypeHandler) { handler = OBJECT_TYPE_HANDLER; } } return handler; } private TypeHandler<?> resolveTypeHandler(ResultSetMetaData rsmd, Integer columnIndex) { TypeHandler<?> handler = null; // 得到 JDBC Type 類型 JdbcType jdbcType = safeGetJdbcTypeForColumn(rsmd, columnIndex); // 得到 Java Type 類型 Class<?> javaType = safeGetClassForColumn(rsmd, columnIndex); //得到對應的 TypeHandler 對象 if (javaType != null && jdbcType != null) { handler = typeHandlerRegistry.getTypeHandler(javaType, jdbcType); } else if (javaType != null) { handler = typeHandlerRegistry.getTypeHandler(javaType); } else if (jdbcType != null) { handler = typeHandlerRegistry.getTypeHandler(jdbcType); } return handler; } }
上面我只是列出其兩個關鍵方法:
resolveTypeHandler(ResultSet rs, String column)
:根據參數類型獲取對應的TypeHandler類型處理器
resolveTypeHandler(ResultSetMetaData rsmd, Integer columnIndex)
:經過ResultSetMetaData獲取某列的Jdbc Type和Java Type,而後獲取對應的TypeHandler類型處理器
org.apache.ibatis.type.TypeHandlerRegistry
:TypeHandler 註冊表,至關於管理 TypeHandler 的容器,從其中能獲取到對應的 TypeHandler 類型處理器,部分代碼以下:
public final class TypeHandlerRegistry { /** * JDBC Type 和 {@link TypeHandler} 的映射 * * {@link #register(JdbcType, TypeHandler)} */ private final Map<JdbcType, TypeHandler<?>> jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class); /** * {@link TypeHandler} 的映射 * * KEY1:Java Type * VALUE1:{@link jdbcTypeHandlerMap} 對象,例如Date對應多種TypeHandler,因此採用Map * KEY2:JDBC Type * VALUE2:{@link TypeHandler} 對象 */ private final Map<Type, Map<JdbcType, TypeHandler<?>>> typeHandlerMap = new ConcurrentHashMap<>(); /** * {@link UnknownTypeHandler} 對象 */ private final TypeHandler<Object> unknownTypeHandler = new UnknownTypeHandler(this); /** * 全部 TypeHandler 的「集合」 * * KEY:{@link TypeHandler#getClass()} * VALUE:{@link TypeHandler} 對象 */ private final Map<Class<?>, TypeHandler<?>> allTypeHandlersMap = new HashMap<>(); /** * 空 TypeHandler 集合的標識,即便 {@link #TYPE_HANDLER_MAP} 中,某個 KEY1 對應的 Map<JdbcType, TypeHandler<?>> 爲空。 * * @see #getJdbcHandlerMap(Type) */ private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = Collections.emptyMap(); /** * 默認的枚舉類型的 TypeHandler 對象 */ private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class; public TypeHandlerRegistry() { register(Boolean.class, new BooleanTypeHandler()); register(boolean.class, new BooleanTypeHandler()); register(JdbcType.BOOLEAN, new BooleanTypeHandler()); register(JdbcType.BIT, new BooleanTypeHandler()); register(JdbcType.CHAR, new StringTypeHandler()); register(JdbcType.VARCHAR, new StringTypeHandler()); register(JdbcType.CLOB, new ClobTypeHandler()); register(JdbcType.LONGVARCHAR, new StringTypeHandler()); register(JdbcType.NVARCHAR, new NStringTypeHandler()); register(JdbcType.NCHAR, new NStringTypeHandler()); register(JdbcType.NCLOB, new NClobTypeHandler()); // ... 省略 ... // issue #273 register(Character.class, new CharacterTypeHandler()); register(char.class, new CharacterTypeHandler()); } public void register(String packageName) { // 掃描指定包下的全部 TypeHandler 類 ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>(); resolverUtil.find(new ResolverUtil.IsA(TypeHandler.class), packageName); Set<Class<? extends Class<?>>> handlerSet = resolverUtil.getClasses(); // 遍歷 TypeHandler 數組,發起註冊 for (Class<?> type : handlerSet) { // Ignore inner classes and interfaces (including package-info.java) and // abstract classes if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) { register(type); } } } }
在構造函數中能夠看到會將 Java 的經常使用類型、JDBC 的類型與對應的 TypeHandler 類型處理器進行映射而後保存起來
提供了register(String packageName)
方法,能夠註冊指定包路徑下的 TypeHandler 類型處理器
org.apache.ibatis.type.TypeAliasRegistry
:類型與別名的註冊表經過別名,主要代碼以下:
public class TypeAliasRegistry { /** * 類型與別名的映射 */ private final Map<String, Class<?>> typeAliases = new HashMap<>(); /** * 初始化默認的類型與別名 * * 另外,在 {@link org.apache.ibatis.session.Configuration} 構造方法中,也有默認的註冊 */ public TypeAliasRegistry() { registerAlias("string", String.class); registerAlias("byte[]", Byte[].class); registerAlias("_byte", byte.class); registerAlias("_byte[]", byte[].class); registerAlias("date", Date.class); registerAlias("date[]", Date[].class); registerAlias("map", Map.class); // ... 省略 ... registerAlias("ResultSet", ResultSet.class); } @SuppressWarnings("unchecked") // throws class cast exception as well if types cannot be assigned public <T> Class<T> resolveAlias(String string) { // 得到別名對應的類型 try { if (string == null) { return null; } // issue #748 // <1> 轉換成小寫 String key = string.toLowerCase(Locale.ENGLISH); Class<T> value; // <2.1> 首先,從 TYPE_ALIASES 中獲取 if (typeAliases.containsKey(key)) { value = (Class<T>) typeAliases.get(key); } else { // <2.2> 其次,直接得到對應類 value = (Class<T>) Resources.classForName(string); } return value; } catch (ClassNotFoundException e) { throw new TypeException("Could not resolve type alias '" + string + "'. Cause: " + e, e); } } /** * 註冊指定包下的別名與類的映射 * * @param packageName 指定包 */ public void registerAliases(String packageName) { registerAliases(packageName, Object.class); } /** * 註冊指定包下的別名與類的映射。另外,要求類必須是 {@param superType} 類型(包括子類)。 * * @param packageName 指定包 * @param superType 指定父類 */ public void registerAliases(String packageName, Class<?> superType) { // 得到指定包下的類門 ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>(); resolverUtil.find(new ResolverUtil.IsA(superType), packageName); Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses(); // 遍歷,逐個註冊類型與別名的註冊表 for (Class<?> type : typeSet) { // Ignore inner classes and interfaces (including package-info.java) // Skip also inner classes. See issue #6 if (!type.isAnonymousClass() // 排除匿名類 && !type.isInterface() // 排除接口 && !type.isMemberClass()) { // 排除內部類 registerAlias(type); } } } public void registerAlias(Class<?> type) { // <1> 默認爲,簡單類名 String alias = type.getSimpleName(); // <2> 若是有註解,使用註冊上的名字 Alias aliasAnnotation = type.getAnnotation(Alias.class); if (aliasAnnotation != null) { alias = aliasAnnotation.value(); } // <3> 註冊類型與別名的註冊表 registerAlias(alias, type); } public void registerAlias(String alias, Class<?> value) { if (alias == null) { throw new TypeException("The parameter alias cannot be null"); } // issue #748 // <1> 轉換成小寫 // 將別名轉換成**小寫**。這樣的話,不管咱們在 Mapper XML 中,寫 `String` 仍是 `string` 甚至是 `STRING` ,都是對應的 String 類型 String key = alias.toLowerCase(Locale.ENGLISH); // <2> 衝突,拋出 TypeException 異常 // 若是已經註冊,而且類型不一致,說明有衝突,拋出 TypeException 異常 if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) { throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + typeAliases.get(key).getName() + "'."); } typeAliases.put(key, value); } public void registerAlias(String alias, String value) { try { registerAlias(alias, Resources.classForName(value)); } catch (ClassNotFoundException e) { throw new TypeException("Error registering type alias " + alias + " for " + value + ". Cause: " + e, e); } } /** * @since 3.2.2 */ public Map<String, Class<?>> getTypeAliases() { return Collections.unmodifiableMap(typeAliases); } }
在構造函數中能夠看到,將 Java 經常使用類型的別名添加到Map<String, Class<?>> typeAliases
對象中
registerAliases(String packageName)
方法,註冊指定包下別名與Class對象的映射
若是使用了@Alias
註解,則獲取其value做爲別名,不然的話使用類的簡單類名
別名都會轉換成小寫,這樣的話,不管咱們在 Mapper XML 中,寫String
仍是string
甚至是STRING
,都是對應的String類型
resolveAlias(String string)
方法,根據參數名稱獲取其Class對象
若是參數名稱在typeAliases
中存在,則直接返回對應的Class對象
不然根據該參數名稱調用Class.forName
方法建立一個Class對象
經過配置別名的方式(指定包路徑或者實體類添加@Alias
註解)能夠簡化咱們的XML映射文件的配置
在別名註冊中心已經提供了Java經常使用類型與Class對象的映射,且映射的key值所有轉換成小寫了,更加便於咱們編寫XML映射文件
提供多種類型處理器,實現了Jdbc Type與Java Type之間的轉換,在TypeHandlerRegistry註冊表中已經初始化好了許多須要用到的類型處理器,便於其餘模塊進行解析
主要包路徑:org.apache.ibatis.io
主要功能:加載類文件以及其餘資源文件,例如加載某個包名下面全部的Class對象
主要看如下幾個類:
org.apache.ibatis.io.ClassLoaderWrapper
:ClassLoader類加載器的包裝器
org.apache.ibatis.io.Resources
:Resources工具類,經過ClassLoaderWrapper獲取資源
org.apache.ibatis.io.ResolverUtil
:解析器工具類,用於得到指定目錄符合條件的Class對象
org.apache.ibatis.io.VFS
:虛擬文件系統(Virtual File System)抽象類,用來查找指定路徑下的文件們
org.apache.ibatis.io.DefaultVFS
:繼承VFS抽象類,默認的VFS實現類
咱們將.java文件編譯成.class文件後,須要經過ClassLoader類加載器將.class文件將其轉換成Class對象,而後"加載"到JVM中,這樣咱們就能夠經過該Class對象建立實例和初始化等操做,其中ClassLoader類加載器使用了雙親委派機制來加載文件
類加載器:
注意:除了啓動類加載器外,全部的類加載器都是ClassLoader的子類,緣由在於啓動類加載器是由C語言而不是Java實現的,ClassLoader中有兩個重要的方法: loadClass(String name,boolean resolve)和findClass(String name), loadClass方法實現雙親委派機制子通常不進行重寫,各子類加載器經過重寫findClass方法實現本身的類加載業務。
能夠看到除了除了啓動類加載器外,每一個類加載器都有父加載器,當一個類加載器加載一個類時,首先會把加載動做委派給他的父加載器,若是父加載器沒法完成這個加載動做時才由該類加載器進行加載。因爲類加載器會向上傳遞加載請求,因此一個類加載時,首先嚐試加載它的確定是啓動類加載器(逐級向上傳遞請求,直到啓動類加載器,它沒有父加載器),以後根據是否能加載的結果逐級讓子類加載器嘗試加載,直到加載成功。
意義:防止加載同一個.class文件、保證核心API不會被篡改
org.apache.ibatis.io.ClassLoaderWrapper
:ClassLoader類加載器的包裝器
在構造器中會獲取系統的構造器,也就是應用類加載器
提供的功能:
說明:都是經過ClassLoader類加載器加載資源的,能夠經過多個ClassLoader進行加載,直到有一個成功則返回資源
org.apache.ibatis.io.Resources
:Resources工具類,經過ClassLoaderWrapper獲取資源
至關於對ClassLoaderWrapper在進行一層包裝,若是沒有獲取到資源則可能拋出異常,還提供更多的方法,例如獲取資源返回Properties對象,獲取資源返回Reader對象,獲取資源返回File對象
org.apache.ibatis.io.ResolverUtil
:解析器工具類,用於得到指定目錄符合條件的Class對象
在經過包名加載Mapper接口,須要使用別名的類,TypeHandler類型處理器類須要使用該工具類
內部定義了一個接口和兩個實現類:
public class ResolverUtil<T> { public interface Test { boolean matches(Class<?> type); } /** * 判斷是匹配指定類 */ public static class IsA implements Test { private Class<?> parent; public IsA(Class<?> parentType) { this.parent = parentType; } /** * Returns true if type is assignable to the parent type supplied in the constructor. */ @Override public boolean matches(Class<?> type) { return type != null && parent.isAssignableFrom(type); } @Override public String toString() { return "is assignable to " + parent.getSimpleName(); } } /** * 判斷是否匹配指定註解 */ public static class AnnotatedWith implements Test { private Class<? extends Annotation> annotation; public AnnotatedWith(Class<? extends Annotation> annotation) { this.annotation = annotation; } /** * Returns true if the type is annotated with the class provided to the constructor. */ @Override public boolean matches(Class<?> type) { return type != null && type.isAnnotationPresent(annotation); } @Override public String toString() { return "annotated with @" + annotation.getSimpleName(); } } }
上面兩個內部類很簡單,用於判斷是否匹配指定類或者指定註解
咱們來看看主要的幾個方法:
public class ResolverUtil<T> { private Set<Class<? extends T>> matches = new HashSet<>(); public Set<Class<? extends T>> getClasses() { return matches; } /** * 獲取包路徑下 parent 的子類 */ public ResolverUtil<T> findImplementations(Class<?> parent, String... packageNames) { if (packageNames == null) { return this; } Test test = new IsA(parent); for (String pkg : packageNames) { find(test, pkg); } return this; } /** * 獲取包路徑下 annotation 的子註解 */ public ResolverUtil<T> findAnnotated(Class<? extends Annotation> annotation, String... packageNames) { if (packageNames == null) { return this; } Test test = new AnnotatedWith(annotation); for (String pkg : packageNames) { find(test, pkg); } return this; } /** * 得到指定包下,符合條件的類 */ public ResolverUtil<T> find(Test test, String packageName) { // <1> 得到包的路徑 String path = getPackagePath(packageName); try { // <2> 得到路徑下的全部文件 List<String> children = VFS.getInstance().list(path); // <3> 遍歷 for (String child : children) { // 是 Java Class if (child.endsWith(".class")) { // 若是匹配,則添加到結果集 addIfMatching(test, child); } } } catch (IOException ioe) { log.error("Could not read package: " + packageName, ioe); } return this; } protected String getPackagePath(String packageName) { return packageName == null ? null : packageName.replace('.', '/'); } @SuppressWarnings("unchecked") protected void addIfMatching(Test test, String fqn) { try { // 得到全類名 String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.'); ClassLoader loader = getClassLoader(); if (log.isDebugEnabled()) { log.debug("Checking to see if class " + externalName + " matches criteria [" + test + "]"); } // 加載類 Class<?> type = loader.loadClass(externalName); // 判斷是否匹配 if (test.matches(type)) { matches.add((Class<T>) type); } } catch (Throwable t) { log.warn("Could not examine class '" + fqn + "'" + " due to a " + t.getClass().getName() + " with message: " + t.getMessage()); } } }
若是咱們須要獲取某個包路徑下的全部類,則能夠設置父類爲java.lang.Object,調用findImplementations
方法便可
再來看到find(Test test, String packageName)
方法,須要調用VFS
的list
方法獲取到全部的文件,而後將匹配的.class文件經過ClassLoader加載成Class對象
org.apache.ibatis.io.VFS
:虛擬文件系統(Virtual File System)抽象類,用來查找指定路徑下的文件們
提供幾個反射的相關方法,list
方法則交由子類實現
默認實現類有JBoss6VFS和DefaultVFS
,固然能夠自定義 VFS 實現類,咱們來看看如何返回VFS實例的,代碼以下:
public abstract class VFS { public static final Class<?>[] IMPLEMENTATIONS = { JBoss6VFS.class, DefaultVFS.class }; // 內置的 VFS 實現類的數組 public static final List<Class<? extends VFS>> USER_IMPLEMENTATIONS = new ArrayList<>(); // 自定義的 VFS 實現類的數組 public static VFS getInstance() { return VFSHolder.INSTANCE; } private static class VFSHolder { static final VFS INSTANCE = createVFS(); @SuppressWarnings("unchecked") static VFS createVFS() { // Try the user implementations first, then the built-ins List<Class<? extends VFS>> impls = new ArrayList<>(); impls.addAll(USER_IMPLEMENTATIONS); impls.addAll(Arrays.asList((Class<? extends VFS>[]) IMPLEMENTATIONS)); // Try each implementation class until a valid one is found VFS vfs = null; // 建立 VFS 對象,選擇最後一個符合的 for (int i = 0; vfs == null || !vfs.isValid(); i++) { Class<? extends VFS> impl = impls.get(i); try { vfs = impl.getDeclaredConstructor().newInstance(); if (!vfs.isValid()) { if (log.isDebugEnabled()) { log.debug("VFS implementation " + impl.getName() + " is not valid in this environment."); } } } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { log.error("Failed to instantiate " + impl, e); return null; } } if (log.isDebugEnabled()) { log.debug("Using VFS adapter " + vfs.getClass().getName()); } return vfs; } } }
能夠看到獲取的VFS實例會進入createVFS()
方法,從提供的幾個VFS實現類中選一個符合的,最後能夠看到會返回最後一個符合的,也就是定義的DefaultVFS
org.apache.ibatis.io.DefaultVFS
:繼承VFS抽象類,默認的VFS實現類
DefaultVFS實現類VFS的list(URL url, String path)
方法,在ResolverUtil
的find
方法中會被調用,因爲代碼冗長,這裏就不列出來了,請自行查看該類😈😈😈
大體邏輯以下:
若是url指向一個JAR Resource,那麼從這個JAR Resource中獲取path路徑下的文件名稱
不然獲取該url中path下面全部的文件,會不斷的遞歸獲取到全部文件(包含咱們須要的.class文件),最後將獲取到的全部文件名稱返回
這樣咱們就能夠在ResolverUtil
將咱們須要的.class文件加載成對應的Class對象
瞭解Java中類加載的雙親委派機制
Mybatis中須要加載類是經過該模塊中的ResolverUtil來實現的,你們能夠先順着org.apache.ibatis.binding.MapperRegistry.addMappers(String packageName)
掃描Mapper接口這個方法看看整個的解析過程
主要包路徑:org.apache.ibatis.logging
主要功能:集成第三方日誌框架,Debug模塊輸出JDBC操做日誌
org.apache.ibatis.logging.LogFactory
:日誌工廠
主要代碼以下:
public final class LogFactory { public static final String MARKER = "MYBATIS"; /** * 使用的 Log 的構造方法 */ private static Constructor<? extends Log> logConstructor; static { // <1> 逐個嘗試,判斷使用哪一個 Log 的實現類,即初始化 logConstructor 屬性 tryImplementation(LogFactory::useSlf4jLogging); tryImplementation(LogFactory::useCommonsLogging); tryImplementation(LogFactory::useLog4J2Logging); tryImplementation(LogFactory::useLog4JLogging); tryImplementation(LogFactory::useJdkLogging); tryImplementation(LogFactory::useNoLogging); } private LogFactory() { // disable construction } public static Log getLog(Class<?> aClass) { return getLog(aClass.getName()); } public static Log getLog(String logger) { try { return logConstructor.newInstance(logger); } catch (Throwable t) { throw new LogException("Error creating logger for logger " + logger + ". Cause: " + t, t); } } public static synchronized void useCustomLogging(Class<? extends Log> clazz) { setImplementation(clazz); } public static synchronized void useSlf4jLogging() { setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class); } public static synchronized void useCommonsLogging() { setImplementation(org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl.class); } public static synchronized void useLog4JLogging() { setImplementation(org.apache.ibatis.logging.log4j.Log4jImpl.class); } public static synchronized void useLog4J2Logging() { setImplementation(org.apache.ibatis.logging.log4j2.Log4j2Impl.class); } private static void tryImplementation(Runnable runnable) { if (logConstructor == null) { try { runnable.run(); } catch (Throwable t) { // ignore } } } private static void setImplementation(Class<? extends Log> implClass) { try { // 得到參數爲 String 的構造方法 Constructor<? extends Log> candidate = implClass.getConstructor(String.class); // 建立 Log 對象 Log log = candidate.newInstance(LogFactory.class.getName()); if (log.isDebugEnabled()) { log.debug("Logging initialized using '" + implClass + "' adapter."); } // 建立成功,意味着可使用,設置爲 logConstructor logConstructor = candidate; } catch (Throwable t) { throw new LogException("Error setting Log implementation. Cause: " + t, t); } } }
在靜態代碼塊中會逐個嘗試,判斷使用哪一個Log
的實現類,即初始化logConstructor
日誌構造器
在getLog(String logger)
方法獲取日誌的實例對象時,經過logConstructor
建立一個實例出來,這樣就完成了日誌的適配
在org.apache.ibatis.logging.jdbc包路徑下有幾個BaseJdbcLogger類,用於DEBUG模式下打印JDBC操做日誌
這裏也使用了JDK動態代理,對JDBC接口進行加強,打印執行日誌,和數據源模塊相似
能夠在org.apache.ibatis.executor.BaseExecutor
的getConnection
方法中看到,若是開啓了DEBUG模式,則建立Connection的動態代理對象,能夠順着下去看
在實際使用MyBatis過程當中,咱們大部分都是使用XML的方式,這樣咱們便於維護
固然MyBatis也提供了許多註解,讓咱們在Mapper接口上面能夠經過註解編寫SQL
主要包路徑:org.apache.ibatis.annotations
用於設置Mapper接口中的方法的參數名稱
若是咱們Mapper接口的方法中有多個入參,咱們須要經過該註解來爲每一個參數設置一個名稱
在反射模塊的ParamNameResolver
工具類中有講到會經過該註解設置參數名稱
用於標記接口爲Mapper接口
在Spring Boot項目中經過mybatis-spring-boot-starter
使用MyBatis時,若是你沒有經過mybatis-spring
子項目中的三種方式(配置MapperScannerConfigurer掃描器、@MapperScan註解或者<mybatis:scan />標籤)配置Mapper接口包路徑,那麼在mybatis-spring-boot
中則會掃描Spring Boot項目設置的基礎包路徑,若是設置了@Mapper
註解,則會當成Mapper接口進行解析,後面的文檔中會講到😈
@Select、@Insert、@Update、@Delete,CURD註解
主要包路徑:org.apache.ibatis.binding
在使用MyBatis時,咱們一般定義Mapper接口,而後在對應的XML映射文件中編寫SQL語句,那麼當咱們調用接口的方法時,爲何能夠執行對應的SQL語句?經過該模塊咱們能夠對它們是如何關聯起來的有個大體的瞭解
Mybatis會自動爲Mapper接口建立一個動態代理實例,經過該動態代理對象的實例進行相關操做
主要涉及到如下幾個類:
org.apache.ibatis.binding.MapperRegistry
:Mapper接口註冊中心,將Mapper接口與其動態代理對象工廠進行保存org.apache.ibatis.binding.MapperProxyFactory
:Mapper接口的動態代理對象工廠,用於生產Mapper接口的動態代理對象org.apache.ibatis.binding.MapperProxy
:Mapper接口的動態代理對象,使用JDK動態代理,實現了java.lang.reflect.InvocationHandler
接口org.apache.ibatis.binding.MapperMethod
:Mapper接口中定義的方法對應的Mapper方法,經過它來執行SQLorg.apache.ibatis.binding.MapperRegistry
:Mapper接口註冊中心,將Mapper接口與其MapperProxyFactory
動態代理對象工廠進行保存
主要代碼以下:
public class MapperRegistry { /** * MyBatis Configuration 對象 */ private final Configuration config; /** * MapperProxyFactory 的映射 * * KEY:Mapper 接口 */ private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>(); @SuppressWarnings("unchecked") public <T> T getMapper(Class<T> type, SqlSession sqlSession) { // <1> 得到 MapperProxyFactory 對象 final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); // 不存在,則拋出 BindingException 異常 if (mapperProxyFactory == null) { throw new BindingException("Type " + type + " is not known to the MapperRegistry."); } try { // 建立 Mapper Proxy 對象 return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); } } public <T> boolean hasMapper(Class<T> type) { return knownMappers.containsKey(type); } public <T> void addMapper(Class<T> type) { // <1> 判斷,必須是接口。 if (type.isInterface()) { // <2> 已經添加過,則拋出 BindingException 異常 if (hasMapper(type)) { throw new BindingException("Type " + type + " is already known to the MapperRegistry."); } boolean loadCompleted = false; try { // <3> 將Mapper接口對應的代理工廠添加到 knownMappers 中 knownMappers.put(type, new MapperProxyFactory<>(type)); // It's important that the type is added before the parser is run // otherwise the binding may automatically be attempted by the mapper parser. // If the type is already known, it won't try. // <4> 解析 Mapper 的註解配置 MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); // 解析 Mapper 接口上面的註解和 Mapper 接口對應的 XML 文件 parser.parse(); // <5> 標記加載完成 loadCompleted = true; } finally { // <6> 若加載未完成,從 knownMappers 中移除 if (!loadCompleted) { knownMappers.remove(type); } } } } /** * @since 3.2.2 */ public Collection<Class<?>> getMappers() { return Collections.unmodifiableCollection(knownMappers.keySet()); } /** * 用於掃描指定包中的Mapper接口,並與XML文件進行綁定 * @since 3.2.2 */ public void addMappers(String packageName, Class<?> superType) { // <1> 掃描指定包下的指定類 ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>(); resolverUtil.find(new ResolverUtil.IsA(superType), packageName); Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses(); // <2> 遍歷,添加到 knownMappers 中 for (Class<?> mapperClass : mapperSet) { addMapper(mapperClass); } } /** * @since 3.2.2 */ public void addMappers(String packageName) { addMappers(packageName, Object.class); } }
addMappers(String packageName, Class<?> superType)
方法:
addMapper(Class<T> type)
進行解析,這裏使用了ResolverUtil工具類,獲取到該包路徑下全部匹配Object.class
的類,這個工具類在IO模塊中已經講過addMapper(Class<T> type)
方法:
建立Mapper接口對應的動態代理對象工廠MapperProxyFactory,添加到knownMappers
中
經過org.apache.ibatis.builder.annotation.MapperAnnotationBuilder
對該接口進行解析,解析對應的XML映射文件(接口名稱+'.xml'文件)
整個的解析過程比較複雜,在後續的《Mybatis的初始化》文檔中進行分析
getMapper(Class<T> type, SqlSession sqlSession)
方法:根據Mapper接口的Class對象和SqlSession對象建立一個動態代理對象
org.apache.ibatis.binding.MapperProxyFactory
:Mapper接口的動態代理對象工廠,用於生產Mapper接口動態代理對象的實例,代碼以下:
public class MapperProxyFactory<T> { /** * Mapper 接口 */ private final Class<T> mapperInterface; /** * 方法與 MapperMethod 的映射 */ private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>(); public MapperProxyFactory(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; } public Class<T> getMapperInterface() { return mapperInterface; } public Map<Method, MapperMethod> getMethodCache() { return methodCache; } @SuppressWarnings("unchecked") protected T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } }
newInstance(SqlSession sqlSession)
方法:建立Mapper接口對應的動態代理對象的實例,能夠看到是經過MapperProxy的構造方法建立了一個動態代理對象的
org.apache.ibatis.binding.MapperProxy
:Mapper接口的動態代理對象,使用JDK動態代理,實現了java.lang.reflect.InvocationHandler
接口,部分代碼以下:
public class MapperProxy<T> implements InvocationHandler, Serializable { /** * SqlSession 對象 */ private final SqlSession sqlSession; /** * Mapper 接口 */ private final Class<T> mapperInterface; /** * 方法與 MapperMethod 的映射 * * 從 {@link MapperProxyFactory#methodCache} 傳遞過來 */ private final Map<Method, MapperMethod> methodCache; public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) { this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; this.methodCache = methodCache; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { // <1> 若是是 Object 定義的方法,直接調用 if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } else if (method.isDefault()) { // 是否有 default 修飾的方法 // 針對Java7以上版本對動態類型語言的支持 if (privateLookupInMethod == null) { return invokeDefaultMethodJava8(proxy, method, args); } else { return invokeDefaultMethodJava9(proxy, method, args); } } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } // <2.1> 得到 MapperMethod 對象 final MapperMethod mapperMethod = cachedMapperMethod(method); // <2.2> 執行 MapperMethod 方法 return mapperMethod.execute(sqlSession, args); } private MapperMethod cachedMapperMethod(Method method) { // 從methodCache緩存中獲取MapperMethod方法,若是爲空則建立一下新的並添加至緩存中 return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())); } }
在這個Mapper接口的動態代理對象中,覆蓋的invoke
方法處理邏輯:
default
修飾符定義他的默認實現ConcurrentHashMap<Method, MapperMethod> methodCache
對象,用於存儲該Mapper接口中方法對應的MapperMethod對象MapperMethod
對象,若是沒有則經過MapperMethod
的構造函數建立一個實例並添加到緩存中MapperMethod
對象,執行相應的SQL操做org.apache.ibatis.binding.MapperMethod
:Mapper接口中定義的方法對應的Mapper方法,經過它來執行SQL,構造方法:
public class MapperMethod { /** * 該方法對應的 SQL 的惟一編號與類型 */ private final SqlCommand command; /** * 該方法的簽名,包含入參和出參的相關信息 */ private final MethodSignature method; public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) { this.command = new SqlCommand(config, mapperInterface, method); this.method = new MethodSignature(config, mapperInterface, method); } }
構造方法初始化了兩個對象,分別爲SqlCommand
和MethodSignature
,都是其內部類
還定義了一個execute(SqlSession sqlSession, Object[] args)
方法,具體的SQL語句執行邏輯,在後續模塊的文檔中進行分析
public class MapperMethod { public static class SqlCommand { /** * SQL的惟一編號:namespace+id(Mapper接口名稱+'.'+方法名稱),{# MappedStatement#id} */ private final String name; /** * SQL 命令類型 {# MappedStatement#sqlCommandType} */ private final SqlCommandType type; public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) { final String methodName = method.getName(); final Class<?> declaringClass = method.getDeclaringClass(); // 獲取該方法對應的 MappedStatement MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass, configuration); if (ms == null) { // 若是有 @Flush 註解,則標記爲 FLUSH 類型 if (method.getAnnotation(Flush.class) != null) { name = null; type = SqlCommandType.FLUSH; } else { throw new BindingException( "Invalid bound statement (not found): " + mapperInterface.getName() + "." + methodName); } } else { name = ms.getId(); type = ms.getSqlCommandType(); if (type == SqlCommandType.UNKNOWN) { throw new BindingException("Unknown execution method for: " + name); } } } private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName, Class<?> declaringClass, Configuration configuration) { // 生成 MappedStatement 惟一編號:接口名稱+'.'+方法名稱 String statementId = mapperInterface.getName() + "." + methodName; // 在全局對象 Configuration 中獲取對應的 MappedStatement if (configuration.hasStatement(statementId)) { return configuration.getMappedStatement(statementId); } else if (mapperInterface.equals(declaringClass)) { // 若是方法就是定義在該接口中,又沒找到則直接返回 null return null; } // 遍歷父接口,獲取對應的 MappedStatement for (Class<?> superInterface : mapperInterface.getInterfaces()) { if (declaringClass.isAssignableFrom(superInterface)) { MappedStatement ms = resolveMappedStatement(superInterface, methodName, declaringClass, configuration); if (ms != null) { return ms; } } } return null; } } }
在構造方法中就是經過statementId(Mapper接口名稱+'.'+方法名稱
)獲取到對應的MappedStatement
對象(在XML映射文件中該方法對應的SQL語句生成的MappedStatement對象),在後續的MyBatis的初始化文檔中會分析該對象是如何建立的
因此咱們定義的XML文件的
namespace
一般是接口名稱,<select />
等節點定義的id就是方法名稱,這樣才能對應起來同一個Mapper接口中不能有重載方法也是這個道理,兩個方法對應同一個statementId就出錯了
獲取到了MappedStatement對象則設置name
爲它的id(Mapper接口名稱+'.'+方法名稱
),type
爲它的SqlCommandType(UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH
)
public class MapperMethod { public static class MethodSignature { /** * 返回數據是否包含多個 */ private final boolean returnsMany; /** * 返回類型是否爲Map的子類,而且該方法上面使用了 @MapKey 註解 */ private final boolean returnsMap; /** * 返回類型是否爲 void */ private final boolean returnsVoid; /** * 返回類型是否爲 Cursor */ private final boolean returnsCursor; /** * 返回類型是否爲 Optional */ private final boolean returnsOptional; /** * 返回類型 */ private final Class<?> returnType; /** * 方法上 @MapKey 註解定義的值 */ private final String mapKey; /** * 用來標記該方法參數列表中 ResultHandler 類型參數得位置 */ private final Integer resultHandlerIndex; /** * 用來標記該方法參數列表中 RowBounds 類型參數得位置 */ private final Integer rowBoundsIndex; /** * ParamNameResolver 對象,主要用於解析 @Param 註解定義的參數,參數值與參數得映射等 */ private final ParamNameResolver paramNameResolver; public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) { // 獲取該方法的返回類型 Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface); if (resolvedReturnType instanceof Class<?>) { this.returnType = (Class<?>) resolvedReturnType; } else if (resolvedReturnType instanceof ParameterizedType) { // 泛型類型 // 獲取該參數化類型的實際類型 this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType(); } else { this.returnType = method.getReturnType(); } // 是否爲無返回結果 this.returnsVoid = void.class.equals(this.returnType); // 返回類型是否爲集合或者數組類型 this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray(); // 返回類型是否爲遊標類型 this.returnsCursor = Cursor.class.equals(this.returnType); // 返回結果是不然 Optional 類型 this.returnsOptional = Optional.class.equals(this.returnType); // 解析方法上面的 @MapKey 註解 this.mapKey = getMapKey(method); this.returnsMap = this.mapKey != null; // 方法參數類型爲 RowBounds 的位置 this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class); // 方法參數類型爲 ResultHandler 的位置 this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class); /* * 解析該方法參數名稱生成參數位置與參數名稱的映射 * @Param 註解則取其值做爲參數名稱,不然取其真實的參數名稱,在沒有則爲參數位置 */ this.paramNameResolver = new ParamNameResolver(configuration, method); } /** * 根據入參返回參數名稱與入參的映射 * * @param args 入參 * @return 參數名稱與入參的映射 */ public Object convertArgsToSqlCommandParam(Object[] args) { return paramNameResolver.getNamedParams(args); } private Integer getUniqueParamIndex(Method method, Class<?> paramType) { Integer index = null; final Class<?>[] argTypes = method.getParameterTypes(); for (int i = 0; i < argTypes.length; i++) { if (paramType.isAssignableFrom(argTypes[i])) { if (index == null) { index = i; } else { throw new BindingException(method.getName() + " cannot have multiple " + paramType.getSimpleName() + " parameters"); } } } return index; } } }
在構造函數中解析Mapper接口中該方法的相關信息並設置到對應的屬性中,會解析出如下信息:
返回值類型
@MapKey註解,使用示例參考:@MapKey註解的使用
參數類型爲 RowBounds 的位置
參數類型爲 ResultHandler 的位置
生成參數名稱解析器 ParamNameResolver
,在反射模塊有講到,用於根據參數值返回參數名稱與參數值的映射關係
每個Mapper接口會有一個MapperProxyFactory
動態代理對象工廠,保存於MapperRegistry
註冊中心
調用接口的方法時,會進入MapperProxy
動態代理對象中,而後經過該方法對應的MapperMethod
方法執行SQL語句的相關操做
疑問:
Mapper接口的動態代理對象會在哪裏建立?
經過DefaultSqlSession的
getMapper(Class<T> type)
方法能夠獲取到Mapper接口的動態代理對象Mapper接口是怎麼做爲對象注入到其餘Spring Bean中?
經過實現BeanDefinitionRegistryPostProcessor接口,修改Mapper接口的BeanDefiniton對象,修改成MapperFactoryBean類型,在FactoryBean中的getObject()方法中獲取到對應的動態代理對象
參考文章:芋道源碼《精盡 MyBatis 源碼分析》