MyBatis技术内幕-笔记

解析器

XML解析方式

XML常用解析方式

Mybatis使用的是DOM解析方式,并结合使用XPath解析XML配置文件,DOM会将整个XML文档加载到内存中并形成树形数据结构,而XPath是一种为查询XML文档而设计的语言,它可以与DOM解析方式配合使用,实现对XML文档的解析。XPath之于XML就好比SQL语言之于数据库。

XPath介绍

XPath(XML Path Language)是一种用于在XML文档中查找信息的语言。它被设计用来处理XML文档中的元素和属性。XPath提供了一种在XML文档中通过路径表达式(类似于文件系统路径)来导航和选择节点(元素、属性、文本等)的方法。

XPath的基本概念

  1. 节点类型
  • 元素节点:XML文档的元素,如 <book>
  • 属性节点:元素的属性,如 id="123"
  • 文本节点:元素或属性的文本内容。
  • 命名空间节点:命名空间,如 xmlns:h="http://www.w3.org/TR/html4/"
  • 处理指令节点:处理指令,如 <?php echo $a; ?>
  • 注释节点:注释,如 <!-- This is a comment -->
  • 文档节点:整个文档根节点。
  1. 路径表达式
  • 绝对路径:以斜杠(/)开头,从根节点选取。
  • 相对路径:不以斜杠开头,从当前节点选取。

XPath语法

XPath使用路径表达式来选择XML文档的节点。路径表达式可以使用绝对路径或相对路径。

常用的XPath表达式

  1. 基本路径表达式
  • /: 选择根节点。
  • /bookstore: 选择根节点 <bookstore>
  • /bookstore/book: 选择根节点 <bookstore> 下的所有 <book> 元素。
  1. 相对路径
  • book: 选择当前节点的所有 <book> 子节点。
  • ./book: 选择当前节点的所有 <book> 子节点(与上面相同)。
  • bookstore/book: 选择 <bookstore> 子节点的所有 <book> 节点。
  1. 通配符
  • *: 选择所有元素节点。
  • @*: 选择所有属性节点。
  • node(): 选择所有类型的节点。
  1. 选择未知的节点
  • //: 选择文档中的所有匹配节点,而不考虑它们的位置。
  • //book: 选择文档中的所有 <book> 元素。
  • bookstore//book: 选择 <bookstore> 元素下的所有 <book> 元素。
  1. 选择属性
  • @: 选择属性。
  • @lang: 选择名为 lang 的属性。
  1. 谓语(Predicates)
  • book[1]: 选择第一个 <book> 元素。
  • book[last()]: 选择最后一个 <book> 元素。
  • book[position() < 3]: 选择位置小于3的前两个 <book> 元素。
  • book[@lang]: 选择具有 lang 属性的 <book> 元素。
  • book[@lang='en']: 选择具有 lang 属性且值为 'en'<book> 元素。
  1. 操作符
  • |: 选择若干个路径。例如,//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表达式

  1. //book[@category='cooking']:选择所有 category 属性值为 cooking<book> 元素。
  2. //book[author='J K. Rowling']:选择所有具有 author 子元素且其值为 J K. Rowling<book> 元素。
  3. //book[price>30.00]:选择所有 price 大于 30.00<book> 元素。
  4. //title[@lang='en']:选择所有 lang 属性值为 en<title> 元素。
  5. /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
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
public enum JdbcType {
/*
* This is added to enable basic support for the ARRAY data type - but a custom type handler is still required
*/
ARRAY(Types.ARRAY),

BIT(Types.BIT),

TINYINT(Types.TINYINT),

SMALLINT(Types.SMALLINT),

INTEGER(Types.INTEGER),

BIGINT(Types.BIGINT),

FLOAT(Types.FLOAT),

REAL(Types.REAL),

DOUBLE(Types.DOUBLE),

NUMERIC(Types.NUMERIC),

DECIMAL(Types.DECIMAL),

CHAR(Types.CHAR),

VARCHAR(Types.VARCHAR),

LONGVARCHAR(Types.LONGVARCHAR),

DATE(Types.DATE),

TIME(Types.TIME),

TIMESTAMP(Types.TIMESTAMP),

BINARY(Types.BINARY),

VARBINARY(Types.VARBINARY),

LONGVARBINARY(Types.LONGVARBINARY),

NULL(Types.NULL),

OTHER(Types.OTHER),

BLOB(Types.BLOB),

CLOB(Types.CLOB),

BOOLEAN(Types.BOOLEAN),

CURSOR(-10), // Oracle

UNDEFINED(Integer.MIN_VALUE + 1000),

NVARCHAR(Types.NVARCHAR), // JDK6

NCHAR(Types.NCHAR), // JDK6

NCLOB(Types.NCLOB), // JDK6

STRUCT(Types.STRUCT),

JAVA_OBJECT(Types.JAVA_OBJECT),

DISTINCT(Types.DISTINCT),

REF(Types.REF),

DATALINK(Types.DATALINK),

ROWID(Types.ROWID), // JDK6

LONGNVARCHAR(Types.LONGNVARCHAR), // JDK6

SQLXML(Types.SQLXML), // JDK6

DATETIMEOFFSET(-155), // SQL Server 2008

TIME_WITH_TIMEZONE(Types.TIME_WITH_TIMEZONE), // JDBC 4.2 JDK8

TIMESTAMP_WITH_TIMEZONE(Types.TIMESTAMP_WITH_TIMEZONE); // JDBC 4.2 JDK8

public final int TYPE_CODE;
private static final Map<Integer, JdbcType> codeLookup = new HashMap<>();

static {
for (JdbcType type : JdbcType.values()) {
codeLookup.put(type.TYPE_CODE, type);
}
}

JdbcType(int code) {
this.TYPE_CODE = code;
}

public static JdbcType forCode(int code) {
return codeLookup.get(code);
}

}

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虚拟机中的类加载器默认使用的是双亲委派机制。三种默认的类加载器分别是:BootstrapClassLoaderExtensionClassLoaderSystemClassLoader(也被称为ApplicationClassLoader),每种类加载器都已经确定从哪个位置加载类文件。

  • BootstrapClassLoader负责加载JDK自带的rt.jar包中的类文件,它是所有类加载器的父加载起,BootstrapClassLoader没有任何父加载器。
  • ExtensionClassLoader负责加载Java的扩展类库,也就是从jre/lib/ext目录下或者java.ext.dirs系统属性指定的目录下加载类。
  • SystemClassLoader负责从classpath环境变量中加载类文件,classpath环境变量通常由”-classpath”或”-cp”命令行选项来定义,或是有JAR中MANIFEST文件的classpath属性指定。SystemClassLoader是ExtensionClassLoader的子加载器。

双亲委派模式可以保证两点:

  1. 子加载器可以使用父加载器已加载的类,而父加载器无法使用子加载器已加载的类;
  2. 父加载器已加载过的类无法被子加载器再次加载

这样就可以保证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接口实现,分别是PooledDataSourceUnpooledDataSource。Mybatis 使用不同的DataSourceFactory 接口实现创建不同类型的DataSource。