精盡MyBatis源碼分析 - 基礎支持層

該系列文檔是本人在學習 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

  • 解析器模塊
  • 反射模塊
  • 異常模塊
  • 數據源模塊
  • 事務模塊
  • 緩存模塊
  • 類型模塊
  • IO模塊
  • 日誌模塊
  • 註解模塊
  • Binding模塊

解析器模塊

主要包路徑: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:動態屬性解析器

XPathParser

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對象中的元素或者節點:

  • eval*元素的方法:根據表達式獲取咱們經常使用類型的元素的值,其中會基於variables調用PropertyParserparse方法替換掉其中的動態值(若是存在),這就是MyBatis如何替換掉XML中的動態值實現的方式
  • eval*節點的方法:根據表達式獲取到org.w3c.dom.Node節點對象,將其封裝成本身定義的XNode對象,方便主要爲了動態值的替換

PropertyParser

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方法替換其中的動態值

GenericTokenParser

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

VariableTokenHandler,是PropertyParser的內部靜態類,變量Token處理器,根據Properties variables變量對象將Token動態值解析成實際值

總結

  • 將XML文件解析成XPathParser對象,其中會解析成對應的Document對象,內部的Properties對象存儲動態變量的值
  • PropertyParser用於解析XML文件中的動態值,根據GenericTokenParser獲取動態屬性的名稱(例如${name}->name),而後經過VariableTokenHandler根據Properties對象獲取到動態屬性(name)對應的值

反射模塊

主要功能:對Java原生的反射進行了良好的封裝,提供更加簡單易用的API,用於解析類對象

反射這一模塊的代碼寫得很漂亮,值得參考!!!

主要包路徑:org.apache.ibatis.reflection

以下所示:

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接口的方法

Reflector

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 全部屬性集合:<屬性名稱(全大寫), 屬性名稱>

MethodInvoker

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;
	}
}
  1. 在其構造函數中,設置set方法或者get方法,並獲取其參數類型或者返回值類型進行保存,也就是該屬性的類型

  2. 若是Class類中有些屬性沒有set或者get方法,那麼這些屬性會被封裝成下面兩個對象(final static修飾的字段不會被封裝),用於設置或者獲取他們的值

    org.apache.ibatis.reflection.invoker.SetFieldInvokerorg.apache.ibatis.reflection.invoker.GetFieldInvoker

DefaultReflectorFactory

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對象,代碼比較簡單

MetaClass

org.apache.ibatis.reflection.MetaClass:Class類的元數據,包裝Reflector,基於PropertyTokenizer(分詞器)提供對Class類的元數據各類操做,能夠理解成對Reflector操做的進一步加強

其中包含了ReflectorFactory和Reflector兩個字段,經過PropertyTokenizer分詞器提供了獲取屬性的名稱和返回值類型等等方法,也就是在Reflector上面新增了一些方法

org.apache.ibatis.reflection.propertyPropertyTokenizer分詞器用於解析相似於'map[qm].user'這樣的屬性,將其分隔保存方便獲取,可閱讀相關代碼哦😈😈😈

DefaultObjectFactory

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對象獲取構造函數,而後經過構造函數建立一個實例對象

BeanWrapper

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對象的方法,這裏不所有羅列出來了,請閱讀其相關代碼進行查看😈😈😈

MetaObject

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對象:

  1. 若是原始Object對象爲null,則返回空的MetaObject對象NULL_META_OBJECT,在SystemMetaObject中定義的一個單例對象,實際就是將MetaObject內部的Object原始對象設置爲NullObject(一個靜態類)

  2. 不然經過構造函數建立MetaObject對象,在它的構造函數中能夠看到,根據Object對象的類型來決定建立什麼類型的ObjectWrapper,並無用到ObjectWrapperFactory工廠接口(默認實現也拋出異常)

對於一個已經初始化好的MetaObject對象,能夠經過getValue方法獲取指定屬性的值,setValue設置指定屬性值

SystemMetaObject

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對象,而後能夠對其屬性進行操做

ParamNameResolver

org.apache.ibatis.reflection.ParamNameResolver:方法參數名稱解析器,用於解析咱們定義的Mapper接口的方法

org.apache.ibatis.binding.MapperMethodMethodSignature內部類會用到

主要代碼以下:

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集合中,大體邏輯:

  1. 若是添加了@Param註解,則參數名稱爲該註解的value值

  2. 沒有添加@Param註解則嘗試獲取真實的參數名

    說明:經過反射獲取方法的參數名,咱們只能獲取到 arg0,arg1 的名稱,由於jdk8以後這些變量名稱沒有被編譯到class文件中,編譯時須要指定-parameters選項,方法的參數名纔會記錄到class文件中,運行時咱們就能夠經過反射機制獲取到

  3. 仍是沒有獲取到參數名則使用序號標記,通常不會走到這一步

還有一個getNamedParams方法,根據實際入參數組返回參數名與參數值的映射,大體邏輯:

  1. 實際參數爲null或者參數個數爲0,則直接返回null

  2. 沒有使用@Param註解而且參數個數爲1,則直接返回參數值

  3. 根據參數順序與參數名的映射獲取到參數名與參數值的映射,並且還會將(param+參數順序)與參數值進行映射,最後將兩種組合的映射返回

總結

  • 將一個Entity實體類或者Map集合轉換成MetaObject對象,該對象經過反射機制封裝了各類簡便的方法,使更加方便安全地操做該對象,建立過程:
  1. 經過Configuration全局配置對象的newMetaObject(Object object)方法建立,會傳入DefaultObjectFactoryDefaultObjectWrapperFactoryDefaultReflectorFactory幾個默認實現類
  2. 內部調用MetaObjectforObject靜態方法,經過它的構造方法建立一個實例對象
  3. 在MetaObject的構造函數中,會根據Object對象的類型來建立ObjectWrapper對象
  4. 若是是建立BeanWrapper,則在其構造函數中,會再調用MetaClass的forClass方法建立MetaClass對象,也就是經過其構造函數建立一個實例對象
  5. 若是是MapWrapper,則直接複製給內部的Map<String, Object> map屬性便可,其餘集合對象相似
  6. 在MetaClass的構造函數中,會經過調用DefaultReflectorFactory的findForClass方法建立Reflector對象
  7. 在Reflector的構造函數中,經過反射機制解析該Class類,屬性的set和get方法會被封裝成MethodInvoker對象
  • ParamNameResolver工具類提供方法參數解析功能(主要解析Mapper接口裏面的方法參數哦~)

異常模塊

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支持三種數據源配置,分別爲UNPOOLEDPOOLEDJNDI。內部提供了兩種數據源實現,分別是UnpooledDataSourcePooledDataSource。在三種數據源配置中,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 對象

UnpooledDataSourceFactory

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)方法:

  1. dataSource建立對應的MetaObject對象,便於設置相應的屬性

  2. 將入參Properties對象中的配置往dataSource設置

  3. 若是是以driver.開頭的配置則統一放入一個Properties中,而後設置到dataSourcedriverProperties屬性中

getDataSource()方法:直接返回建立好的數據源dataSource

UnpooledDataSource

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)方法:

  1. 初始化Driver,將其包裝成DriverProxy,主要用於使用本身的Logger對象
  2. 得到 Connection 對象
  3. 配置 Connection 對象,網絡超時時間、是否自動提交事務、默認的事務隔離級別

該類還包含了數據庫鏈接的基本信息以及其餘方法,我這裏沒有所有列出來,感興趣的能夠看下這個類😈

PooledDataSourceFactory

org.apache.ibatis.datasource.pooled.PooledDataSourceFactory:繼承 UnpooledDataSourceFactory 類,池化的 DataSourceFactory 實現類

和 UnpooledDataSourceFactory 的區別是它建立了 PooledDataSource 對象

PooledDataSource

org.apache.ibatis.datasource.pooled.PooledDataSource:實現 DataSource 接口,池化的 DataSource 實現類,包含數據庫的鏈接信息以及鏈接池的配置信息

下面的方法都有點長,我就】就不列出來了,能夠根據流程圖並結合註釋進行查看😈

getConnection()方法:用於獲取鏈接,流程圖:

PolledDataSource-popConnection

pushConnection方法:"關閉"一個鏈接,放入空閒鏈接隊列中或者關設置爲無效狀態並關閉真正的鏈接,在PooledConnectioninvoke方法中,若是方法名稱爲close,表示須要關閉該鏈接,則調用該方法,流程圖:

PooledDataSource-pushConnection

pingConnection方法:經過向數據庫發起 poolPingQuery 語句來發起"ping"操做,以判斷數據庫鏈接是否有效,在PooledConnectionisValid方法中被調用,PooledDataSource的獲取鏈接和關閉鏈接時都會判斷該鏈接是否有效

forceCloseAll方法:用於關閉全部的鏈接,釋放資源,在定義的finalize()方法中會被調用,即當前PooledDataSource對象被釋放時

PoolState

org.apache.ibatis.datasource.pooled.PoolState:鏈接池狀態,記錄空閒和激活的PooledConnection集合,以及相關的數據統計

至關於一個池子,保存了空閒的或者正在被使用的鏈接以及一些鏈接池的配置信息

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,代理的時候進行了如下操做:

  1. 調用Connection的close方法時,有時候不用真正的關閉,須要進行一些處理

  2. 調用Connection其餘方法以前,須要先檢測該鏈接的可用性,而後再執行該方法

總結

實際咱們不會用到Mybatis內部的數據源,都是經過Druid或者HikariCP等第三方組件,這裏咱們來看看PooledConnection使用的JDK動態代理

PooledConnection實現了InvocationHandler接口,在invoke方法中進行了預處理,例如檢查鏈接是否有效,而後在經過真實的Connection操做

在它的構造方法中初始化了代理類實例:

this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);

經過java.lang.reflect.ProxynewProxyInstance方法實例化代理類對象,入參分別是類加載器,代理的接口,調用處理程序

在使用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

主要功能:

  • 簡化配置文件提供別名機制
  • 實現 Jdbc Type 與 Java 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 映射文件中配置 resultTypeparameterType 屬性時,直接配置爲別名而不用寫全類名

TypeHandler

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 的過程

BaseTypeHandler

org.apache.ibatis.type.BaseTypeHandler:實現 TypeHandler 接口,繼承 TypeReference 抽象類,TypeHandler 基礎抽象類

在方法的實現中捕獲異常,內部調用抽象方,交由子類去實現

IntegerTypeHandler

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;
	}
}

UnknownTypeHandler

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類型處理器

TypeHandlerRegistry

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 類型處理器

TypeAliasRegistry

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對象的映射

  1. 若是使用了@Alias註解,則獲取其value做爲別名,不然的話使用類的簡單類名

  2. 別名都會轉換成小寫,這樣的話,不管咱們在 Mapper XML 中,寫String仍是string甚至是STRING ,都是對應的String類型

resolveAlias(String string)方法,根據參數名稱獲取其Class對象

  1. 若是參數名稱在typeAliases中存在,則直接返回對應的Class對象

  2. 不然根據該參數名稱調用Class.forName方法建立一個Class對象

總結

經過配置別名的方式(指定包路徑或者實體類添加@Alias註解)能夠簡化咱們的XML映射文件的配置

在別名註冊中心已經提供了Java經常使用類型與Class對象的映射,且映射的key值所有轉換成小寫了,更加便於咱們編寫XML映射文件

提供多種類型處理器,實現了Jdbc Type與Java Type之間的轉換,在TypeHandlerRegistry註冊表中已經初始化好了許多須要用到的類型處理器,便於其餘模塊進行解析

IO模塊

主要包路徑: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
  • 啓動類加載器:爲null,JVM啓動時建立,由HotSpot實現,負責加載jre/lib/rt.jar包下的核心類
  • 擴展類加載器:定義在sun.misc.Launcher的一個內部類,sun.misc.Launcher$ExtClassLoader,負責加載jre/lib/ext/*.jar包下的擴展類
  • 應用類加載器:定義在sun.misc.Launcher的一個內部類,sun.misc.Launcher$AppClassLoader,負責加載ClassPath路徑下面的類
  • 自定義類加載器:用戶自定義的類加載器,能夠經過重寫findClass()方法進行加載類

注意:除了啓動類加載器外,全部的類加載器都是ClassLoader的子類,緣由在於啓動類加載器是由C語言而不是Java實現的,ClassLoader中有兩個重要的方法: loadClass(String name,boolean resolve)和findClass(String name), loadClass方法實現雙親委派機制子通常不進行重寫,各子類加載器經過重寫findClass方法實現本身的類加載業務。

能夠看到除了除了啓動類加載器外,每一個類加載器都有父加載器,當一個類加載器加載一個類時,首先會把加載動做委派給他的父加載器,若是父加載器沒法完成這個加載動做時才由該類加載器進行加載。因爲類加載器會向上傳遞加載請求,因此一個類加載時,首先嚐試加載它的確定是啓動類加載器(逐級向上傳遞請求,直到啓動類加載器,它沒有父加載器),以後根據是否能加載的結果逐級讓子類加載器嘗試加載,直到加載成功。

意義:防止加載同一個.class文件、保證核心API不會被篡改

ClassLoaderWrapper

org.apache.ibatis.io.ClassLoaderWrapper:ClassLoader類加載器的包裝器

在構造器中會獲取系統的構造器,也就是應用類加載器

提供的功能:

  • 獲取一個資源返回URL
  • 獲取一個資源返回InputStream
  • 獲取指定類名的Class對象

說明:都是經過ClassLoader類加載器加載資源的,能夠經過多個ClassLoader進行加載,直到有一個成功則返回資源

Resources

org.apache.ibatis.io.Resources:Resources工具類,經過ClassLoaderWrapper獲取資源

至關於對ClassLoaderWrapper在進行一層包裝,若是沒有獲取到資源則可能拋出異常,還提供更多的方法,例如獲取資源返回Properties對象,獲取資源返回Reader對象,獲取資源返回File對象

ResolverUtil

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)方法,須要調用VFSlist方法獲取到全部的文件,而後將匹配的.class文件經過ClassLoader加載成Class對象

VFS

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

DefaultVFS

org.apache.ibatis.io.DefaultVFS:繼承VFS抽象類,默認的VFS實現類

DefaultVFS實現類VFS的list(URL url, String path)方法,在ResolverUtilfind方法中會被調用,因爲代碼冗長,這裏就不列出來了,請自行查看該類😈😈😈

大體邏輯以下:

  1. 若是url指向一個JAR Resource,那麼從這個JAR Resource中獲取path路徑下的文件名稱

  2. 不然獲取該url中path下面全部的文件,會不斷的遞歸獲取到全部文件(包含咱們須要的.class文件),最後將獲取到的全部文件名稱返回

這樣咱們就能夠在ResolverUtil將咱們須要的.class文件加載成對應的Class對象

總結

瞭解Java中類加載的雙親委派機制

Mybatis中須要加載類是經過該模塊中的ResolverUtil來實現的,你們能夠先順着org.apache.ibatis.binding.MapperRegistry.addMappers(String packageName)掃描Mapper接口這個方法看看整個的解析過程

日誌模塊

主要包路徑:org.apache.ibatis.logging

主要功能:集成第三方日誌框架,Debug模塊輸出JDBC操做日誌

LogFactory

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);
		}
	}
}
  1. 在靜態代碼塊中會逐個嘗試,判斷使用哪一個Log的實現類,即初始化logConstructor日誌構造器

  2. getLog(String logger)方法獲取日誌的實例對象時,經過logConstructor建立一個實例出來,這樣就完成了日誌的適配

BaseJdbcLogger

在org.apache.ibatis.logging.jdbc包路徑下有幾個BaseJdbcLogger類,用於DEBUG模式下打印JDBC操做日誌

這裏也使用了JDK動態代理,對JDBC接口進行加強,打印執行日誌,和數據源模塊相似

能夠在org.apache.ibatis.executor.BaseExecutorgetConnection方法中看到,若是開啓了DEBUG模式,則建立Connection的動態代理對象,能夠順着下去看

註解模塊

在實際使用MyBatis過程當中,咱們大部分都是使用XML的方式,這樣咱們便於維護

固然MyBatis也提供了許多註解,讓咱們在Mapper接口上面能夠經過註解編寫SQL

主要包路徑:org.apache.ibatis.annotations

@Param

用於設置Mapper接口中的方法的參數名稱

若是咱們Mapper接口的方法中有多個入參,咱們須要經過該註解來爲每一個參數設置一個名稱

反射模塊ParamNameResolver工具類中有講到會經過該註解設置參數名稱

@Mapper

用於標記接口爲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註解

Binding模塊

主要包路徑: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方法,經過它來執行SQL

MapperRegistry

org.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)方法:

  1. 獲取該包路徑下的Mapper接口Class對象,而後調用addMapper(Class<T> type)進行解析,這裏使用了ResolverUtil工具類,獲取到該包路徑下全部匹配Object.class的類,這個工具類在IO模塊中已經講過

addMapper(Class<T> type)方法:

  1. 建立Mapper接口對應的動態代理對象工廠MapperProxyFactory,添加到knownMappers

  2. 經過org.apache.ibatis.builder.annotation.MapperAnnotationBuilder對該接口進行解析,解析對應的XML映射文件(接口名稱+'.xml'文件)

    整個的解析過程比較複雜,在後續的《Mybatis的初始化》文檔中進行分析

getMapper(Class<T> type, SqlSession sqlSession)方法:根據Mapper接口的Class對象和SqlSession對象建立一個動態代理對象

  1. 根據Mapper接口的Class對象獲取對應的MapperProxyFactory工廠
  2. 經過MapperProxyFactory工廠建立一個動態代理對象實例

MapperProxyFactory

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的構造方法建立了一個動態代理對象的

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方法處理邏輯:

  1. 若是是Object.class定義的方法,則直接調用
  2. 若是該方法有默認實現則調用其默認實現,在jdk8中支持接口中的方法能夠經過default修飾符定義他的默認實現
  3. 不然的話,獲取該Mapper接口中的該方法對應的MapperMethod對象
    1. 工廠中定義了一個ConcurrentHashMap<Method, MapperMethod> methodCache對象,用於存儲該Mapper接口中方法對應的MapperMethod對象
    2. 根據方法獲取MapperMethod對象,若是沒有則經過MapperMethod的構造函數建立一個實例並添加到緩存中
  4. 經過Mapper接口中該方法對應的MapperMethod對象,執行相應的SQL操做

MapperMethod

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);
	}
}

構造方法初始化了兩個對象,分別爲SqlCommandMethodSignature,都是其內部類

還定義了一個execute(SqlSession sqlSession, Object[] args)方法,具體的SQL語句執行邏輯,在後續模塊的文檔中進行分析

SqlCommand
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

MethodSignature
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語句的相關操做

疑問:

  1. Mapper接口的動態代理對象會在哪裏建立?

    經過DefaultSqlSession的getMapper(Class<T> type)方法能夠獲取到Mapper接口的動態代理對象

  2. Mapper接口是怎麼做爲對象注入到其餘Spring Bean中?

    經過實現BeanDefinitionRegistryPostProcessor接口,修改Mapper接口的BeanDefiniton對象,修改成MapperFactoryBean類型,在FactoryBean中的getObject()方法中獲取到對應的動態代理對象

參考文章:芋道源碼《精盡 MyBatis 源碼分析》

相關文章
相關標籤/搜索