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

该系列文档是本人在学习 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

如下所示:

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

上一篇:阿里巴巴Java方向面试题汇总(含答案)


下一篇:该驱动程序不支持 SQL Server 8 版