25

精尽 MyBatis 源码分析 - 基础支持层

 3 years ago
source link: http://www.cnblogs.com/lifullmoon/p/14014934.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释( Mybatis源码分析 GitHub 地址Mybatis-Spring 源码分析 GitHub 地址Spring-Boot-Starter 源码分析 GitHub 地址 )进行阅读

MyBatis 版本:3.5.2

MyBatis-Spring 版本:2.0.3

MyBatis-Spring-Boot-Starter 版本:2.1.4

基础支持层

《精尽 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

如下所示:

NzAb6fa.png!mobile

主要查看以下几个类:

  • 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类,整个解析过程并不复杂,我这里就不全部讲述了,可阅读相关代码,已做好注释:smiling_imp::smiling_imp::smiling_imp:

解析后保存了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'这样的属性,将其分隔保存方便获取,可阅读相关代码哦:smiling_imp::smiling_imp::smiling_imp:

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 对象的方法,这里不全部罗列出来了,请阅读其相关代码进行查看:smiling_imp::smiling_imp::smiling_imp:

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
org.apache.ibatis.datasource.unpooled.UnpooledDataSource
org.apache.ibatis.datasource.pooled.PooledDataSourceFactory
org.apache.ibatis.datasource.pooled.PooledDataSource
org.apache.ibatis.datasource.pooled.PoolState
org.apache.ibatis.datasource.pooled.PooledConnection

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 对象,网络超时时间、是否自动提交事务、默认的事务隔离级别

该类还包含了数据库连接的基本信息以及其他方法,我这里没有全部列出来,感兴趣的可以看下这个类:smiling_imp:

PooledDataSourceFactory

org.apache.ibatis.datasource.pooled.PooledDataSourceFactory :继承 UnpooledDataSourceFactory 类,池化的 DataSourceFactory 实现类

和 UnpooledDataSourceFactory 的区别是它创建了 PooledDataSource 对象

PooledDataSource

org.apache.ibatis.datasource.pooled.PooledDataSource :实现 DataSource 接口,池化的 DataSource 实现类,包含数据库的连接信息以及连接池的配置信息

下面的方法都有点长,我就】就不列出来了,可以根据流程图并结合注释进行查看:smiling_imp:

getConnection() 方法:用于获取连接,流程图:

iuENVrM.png!mobile

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

3YZjiuZ.png!mobile

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对象

typeAliases
Class.forName

总结

通过配置别名的方式(指定包路径或者实体类添加 @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类加载器使用了双亲委派机制来加载文件

类加载器:

jMvimaQ.png!mobile
  • 启动类加载器:为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 方法中会被调用,由于代码冗长,这里就不列出来了,请自行查看该类:smiling_imp::smiling_imp::smiling_imp:

大致逻辑如下:

  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接口进行解析,后面的文档中会讲到:smiling_imp:

其他注解

@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 源码分析》


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK