MyBatis技术内幕-笔记
解析器
XML解析方式
Mybatis使用的是DOM解析方式,并结合使用XPath解析XML配置文件,DOM会将整个XML文档加载到内存中并形成树形数据结构,而XPath是一种为查询XML文档而设计的语言,它可以与DOM解析方式配合使用,实现对XML文档的解析。XPath之于XML就好比SQL语言之于数据库。
XPath介绍
XPath(XML Path Language)是一种用于在XML文档中查找信息的语言。它被设计用来处理XML文档中的元素和属性。XPath提供了一种在XML文档中通过路径表达式(类似于文件系统路径)来导航和选择节点(元素、属性、文本等)的方法。
XPath的基本概念
- 节点类型:
- 元素节点:XML文档的元素,如
<book>
。- 属性节点:元素的属性,如
id="123"
。- 文本节点:元素或属性的文本内容。
- 命名空间节点:命名空间,如
xmlns:h="http://www.w3.org/TR/html4/"
。- 处理指令节点:处理指令,如
<?php echo $a; ?>
。- 注释节点:注释,如
<!-- This is a comment -->
。- 文档节点:整个文档根节点。
- 路径表达式:
- 绝对路径:以斜杠(/)开头,从根节点选取。
- 相对路径:不以斜杠开头,从当前节点选取。
XPath语法
XPath使用路径表达式来选择XML文档的节点。路径表达式可以使用绝对路径或相对路径。
常用的XPath表达式
- 基本路径表达式:
/
: 选择根节点。/bookstore
: 选择根节点<bookstore>
。/bookstore/book
: 选择根节点<bookstore>
下的所有<book>
元素。
- 相对路径:
book
: 选择当前节点的所有<book>
子节点。./book
: 选择当前节点的所有<book>
子节点(与上面相同)。bookstore/book
: 选择<bookstore>
子节点的所有<book>
节点。
- 通配符:
*
: 选择所有元素节点。@*
: 选择所有属性节点。node()
: 选择所有类型的节点。
- 选择未知的节点:
//
: 选择文档中的所有匹配节点,而不考虑它们的位置。//book
: 选择文档中的所有<book>
元素。bookstore//book
: 选择<bookstore>
元素下的所有<book>
元素。
- 选择属性:
@
: 选择属性。@lang
: 选择名为lang
的属性。
- 谓语(Predicates):
book[1]
: 选择第一个<book>
元素。book[last()]
: 选择最后一个<book>
元素。book[position() < 3]
: 选择位置小于3的前两个<book>
元素。book[@lang]
: 选择具有lang
属性的<book>
元素。book[@lang='en']
: 选择具有lang
属性且值为'en'
的<book>
元素。
- 操作符:
|
: 选择若干个路径。例如,//book | //cd
选择文档中的所有<book>
和<cd>
元素。+
,-
,*
,div
,=
,!=
,<
,<=
,>
,>=
: 数学和比较操作符。XPath函数
XPath 还支持许多函数来处理字符串、数值、日期、时间等。
string-length(string)
: 返回字符串的长度。concat(string1, string2, ...)
: 连接多个字符串。contains(string, substring)
: 如果字符串包含子字符串,则返回true
。starts-with(string, substring)
: 如果字符串以子字符串开始,则返回true
。not(boolean)
: 取反。示例XML文档
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 >xml
>复制代码
><bookstore>
<book category="cooking">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
<book category="children">
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category="web">
<title lang="en">XQuery Kick Start</title>
<author>James McGovern</author>
<author>Per Bothner</author>
<author>Kurt Cagle</author>
<author>James Linn</author>
<author>Vaidyanathan Nagarajan</author>
<year>2003</year>
<price>49.99</price>
</book>
<book category="web" cover="paperback">
<title lang="en">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
></bookstore>示例XPath表达式
//book[@category='cooking']
:选择所有category
属性值为cooking
的<book>
元素。//book[author='J K. Rowling']
:选择所有具有author
子元素且其值为J K. Rowling
的<book>
元素。//book[price>30.00]
:选择所有price
大于30.00
的<book>
元素。//title[@lang='en']
:选择所有lang
属性值为en
的<title>
元素。/bookstore/book[1]
:选择<bookstore>
根元素的第一个<book>
子元素。XPath 是一个强大的工具,广泛应用于处理和查询 XML 数据。通过结合路径表达式和函数,可以高效地从复杂的 XML 文档中提取所需的信息。
XPathParser
MyBatis提供的XPathParser类提供了XML的解析。
反射
MyBatis在进行参数处理、结果映射等操作时,会涉及大量的反射操作。Java中的反射虽然功能强大,但是代码编写起来比较复杂且容易出错,为了简化反射操作的相关代码,MyBatis提供了专门的反射模块,该模块位于org.apache.ibatis.reflection
包中,它对常见的反射操作做了进一步封装,提供了更加简洁方便的反射API。
Reflector & ReflectorFactory
Reflector是MyBatis中反射模块的基础,每个Reflector对象都对应一个类,在Reflector中缓存了反射操作需要使用的类的元信息。
ReflectorFactory接口主要实现了对Reflector对象的创建和缓存。
TypeParameterResolver
它是一个工具类,提供一系列静态方法来解析指定类中的字段、方法返回值或方法参数的类型。
Type接口
Type是所有类型的父接口,它有四个子接口和一个实现类。
- Class:表示的是原始类型。Class类的对象表示JVM中的一个类或接口,每个Java类在JVM里都表现为一个Class对象。
- ParameterizedType:表示的是参数化类型,如List
、Map<Integer, String>这种带有泛型的类型。 - TypeVariable:表示的是类型变量,它用来反映在JVM编译该泛型前的信息,例如List
中的T就是类型变量。 - GenericArrayType:表示的是数组类型且组成元素是ParameterizedType或TypeVariable。例如List
[]或T[]。 - WildcardType:表示的是通配符泛型,例如
? extends Number
和?super Integer
ObjectFactory
通过create()方法可以创建指定类型的对象。
Property工具集
- PropertyTokenizer:解析属性表达式
- PropertyName:完成方法名到属性名的转换
- PropertyCopier:属性拷贝工具
MetaClass
MetaClass提供Reflector和PropertyTokenizer组合使用,实现了对复杂的属性表达式的解析,并实现了获取指定属性描述信息的功能。
ObjectWrapper
MetaClass是MyBatis对类级别的元信息的封装和处理。ObjectWrapper接口是对对象的包装,抽象了对象的属性信息,它定义了一系列查询对象属性信息的方法,以及更新属性的方法。
MetaObject
MetaObject完成属性表达式的解析。
类型转换
JDBC数据类型与Java语言中的数据类型并不是完全对应的,所以在PreparedStatement为SQL语句绑定参数时,需要从Java类型转换成JDBC类型,而从结果集中获取数据时,则需要从JDBC类型转换成Java类型。MyBatis使用类型处理器完成上述两种转换。
JdbcType枚举
1 | public enum JdbcType { |
TypeHandler
MyBatis中所有的类型转换器都继承了TypeHandler接口,接口中定义了四个方法,分为两类:setParameter()方法负责将数据由JdbcType类型转换为Java类型;getResult()方法及其重载方法负责将数据由Java类型转换成JdbcType类型。
TypeHandlerRegistry
在MyBatis初始化过程中,会为所有已知的TypeHandler创建对象,并实现注册到TypeHandlerRegistry中,由TypeHandlerRegistry负责管理这些TypeHandler对象。
TypeAliasRegistry
在编写SQL语句时,使用别名可以方便理解以及维护,例如表名或列名很长时,我们一般会为其设计易懂易维护的别名。MyBatis将SQL语句中别名的概念进行了延伸和扩展,MyBatis可以为一个类添加一个别名,之后就可以通过别名引用该类。
MyBatis通过TypeAliasRegistry类完成别名注册和管理的功能。
日志模块
日志适配器
日志模块位于org.apache.ibatis.logging
包中,该模块中通过Log接口定义了日志模块的功能。LogFactory工厂负责创建对应的日志组件适配器,在LogFactory类加载时会执行其静态代码块,其逻辑是按序加载并实例化对于日志组件的适配器,然后使用LogFactory.logConstructor这个静态字段,记录当前使用的第三方日志组件的适配器。
JDBC调试
在MyBatis的日志模块中有一个Jdbc包,它并不是将日志信息通过JDBC保存在数据库中,而是通过JDK动态代理的方式,将JDBC操作通过指定的日志框架打印出来。这个功能通常在开发阶段使用,它可以输出SQL语句、用户传入的绑定参数、SQL语句影响行数等等信息,对调试程序来说是非常重要的。
BaseJdbcLogger
BaseJdbcLogger是一个抽象类,它是Jdbc包下其他Logger类的父类。
资源加载
类加载器简介
Java虚拟机中的类加载器负责加载来自文件系统、网络或其他来源的类文件。Java虚拟机中的类加载器默认使用的是双亲委派机制。三种默认的类加载器分别是:BootstrapClassLoader、ExtensionClassLoader、SystemClassLoader(也被称为ApplicationClassLoader),每种类加载器都已经确定从哪个位置加载类文件。
- BootstrapClassLoader负责加载JDK自带的rt.jar包中的类文件,它是所有类加载器的父加载起,BootstrapClassLoader没有任何父加载器。
- ExtensionClassLoader负责加载Java的扩展类库,也就是从jre/lib/ext目录下或者java.ext.dirs系统属性指定的目录下加载类。
- SystemClassLoader负责从classpath环境变量中加载类文件,classpath环境变量通常由”-classpath”或”-cp”命令行选项来定义,或是有JAR中MANIFEST文件的classpath属性指定。SystemClassLoader是ExtensionClassLoader的子加载器。
双亲委派模式可以保证两点:
- 子加载器可以使用父加载器已加载的类,而父加载器无法使用子加载器已加载的类;
- 父加载器已加载过的类无法被子加载器再次加载
这样就可以保证JVM的安全性和稳定性。
除了系统提供的三种类加载器,开发人员也可以通过继承java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求。
ClassLoaderWrapper
在MyBatis的IO包中封装了ClassLoader以及读取资源文件的相关API。
在IO包中提供的ClassLoaderWrapper是一个ClassLoader的包装器,其中包含了多个ClassLoader对象。通过调整多个类加载器的使用顺序,ClassLoaderWrapper可以确保返回给系统使用的是正确的类加载器。使用ClassLoaderWrapper就如同使用一个ClassLoader对象,ClassLoaderWrapper会按照指定的顺序依次检测其中封装的ClassLoader对象,并从中选取第一个可用的ClassLoader完成相关功能。
ResolverUtil
ResolverUtil可以根据指定的条件查找指定包下的类,其中使用的条件由Test接口表示。ResolverUtil中使用classLoader字段记录了当前使用的类加载器,默认情况下,使用的是当前线程上下文绑定的类加载器,可通过setClassLoader()方法修改使用类加载器。
VFS
VFS表示虚拟文件系统,它用来查找指定路径下的资源。VFS是一个抽象类,MyBatis中提供了JBoss6VFS和DefaultVFS两个VFS的实现。
在ResolverUtil.find()方法查找类文件时会调用list()方法的重载方法。
DataSource
在数据持久层中,数据源是一个非常重要的组件,其性能直接关系到整个数据持久层的性能 。在实践中比较常见的第三方数据源组件有 Apache Common DBCP、C3PO 、 Proxool 等, MyBatis不仅可以集成第三方数据源组件,还提供了自己的数据源实现。
常见的数据源组件都实现了javax.sql.DataSource
接口,MyBatis 自身实现的数据源实现也不例外。 MyBatis 提供了两个javax.sql.DataSource
接口实现,分别是PooledDataSource
和UnpooledDataSource
。Mybatis 使用不同的DataSourceFactory 接口实现创建不同类型的DataSource。