基础支持层

《精尽 MyBatis 源码分析 - 整体架构》中对 MyBatis 的基础支持层已做过介绍,包含整个 MyBatis 的基础模块,为核心处理层的功能提供了良好的支撑,本文对基础支持层的每个模块进行分析

  • 解析器模块
  • 反射模块
  • 异常模块
  • 数据源模块
  • 事务模块
  • 缓存模块
  • 类型模块
  • IO模块
  • 日志模块
  • 注解模块
  • Binding模块

解析器模块

主要包路径:org.apache.ibatis.parsing

主要功能:初始化时解析mybatis-config.xml配置文件、为处理动态SQL语句中占位符提供支持

主要查看以下几个类:

  • org.apache.ibatis.parsing.XPathParser:基于Java XPath 解析器,用于解析MyBatis的mybatis-config.xml和**Mapper.xml等XML配置文件
  • org.apache.ibatis.parsing.GenericTokenParser:通用的Token解析器
  • 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

如下所示:

MyBatis 源码分析 - 基础支持层_解析器

主要查看以下几个类:

  • 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 的名称,这些变量名称没有被编译到class文件中,在jdk8之后,编译时我们可以指定-parameters选项,方法的参数名才会记录到class文件中,运行时我们就可以通过反射机制获取到

  1. 还是没有获取到参数名则使用序号标记,一般不会走到这一步

还有一个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()方法:用于获取连接,流程图:

MyBatis 源码分析 - 基础支持层_解析器_02

pushConnection方法:"关闭"一个连接,放入空闲连接队列中或者关设置为无效状态并关闭真正的连接,在PooledConnectioninvoke方法中,如果方法名称为close,表示需要关闭该连接,则调用该方法,流程图:

MyBatis 源码分析 - 基础支持层_apache_03

MyBatis 源码分析 - 基础支持层_XML_04

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 映射文件中配置 resultType 和 parameterType 属性时,直接配置为别名而不用写全类名

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类加载器使用了双亲委派机制来加载文件

类加载器:

MyBatis 源码分析 - 基础支持层_解析器_05

  • 启动类加载器:为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的构造函数创建一个实例并添加到缓存中
  1. 通过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()方法中获取到对应的动态代理对象