type包
type包中的类有 55个之多。在遇到这种繁杂的情况时,一定要注意归类总结。
归类总结是源码阅读中非常好的办法。往往越是大量的类,越是大量的方法,越有规律进行分类。这些原本繁杂的类和方法经过分类后,可能会变得很有条理。
经过梳理后,我们把 type包内的类分为以下六组。
· 类型处理器:1个接口、1个基础实现类、1个辅助类、43个实现类。
-TypeHandler:类型处理器接口;
-BaseTypeHandler:类型处理器的基础实现;
-TypeReference:类型参考器;
-*TypeHandler:43个类型处理器。
· 类型注册表:3个。
-SimpleTypeRegistry:基本类型注册表,内部使用 Set 维护了所有 Java 基本数据类型的集合;
-TypeAliasRegistry:类型别名注册表,内部使用 HashMap维护了所有类型的别名和类型的映射关系;
-TypeHandlerRegistry:类型处理器注册表,内部维护了所有类型与对应类型处理器的映射关系。
· 注解类:3个。
-Alias:使用该注解可以给类设置别名,设置后,别名和类型的映射关系便存入TypeAliasRegistry中;
-MappedJdbcTypes:有时我们想使用自己的处理器来处理某些 JDBC 类型,只需创建 BaseTypeHandler 的子类,然后在上面加上该注解,声明它要处理的JDBC类型即可;
-MappedTypes:有时我们想使用自己的处理器来处理某些Java类型,只需创建BaseTypeHandler的子类,然后在上面加上该注解,声明它要处理的 Java类型即可。
· 异常类:1个。
-TypeException:表示与类型处理相关的异常。
· 工具类:1个。
-ByteArrayUtils:提供数组转化的工具方法。
· 枚举类:1个。
-JdbcType:在 Enum中定义了所有的 JDBC类型,类型来源于 java.sql.Types。
以上类中,注解类、异常类、工具类、枚举类都非常简单,不再单独介绍。下面将重点介绍类型处理器和类型注册表。
模板模式
说起模板大家应该都很熟悉。一般情况下,模板中规定了大体的框架,只留下一些细节供使用者来修改和完善。使用同一模板做出的不同产品都具有一致的框架。设计模式中的模板模式与上述模板的概念相同。在模板模式中,需要使用一个抽象类定义一套操作的整体步骤(即模板),而抽象类的子类则完成每个步骤的具体实现。这样,抽象类的不同子类遵循了同样的一套模板。
例如,我们定义一套打扫卫生的模板,如代码8-1 所示。它为所有的打扫卫生工作定义了四个大的步骤:准备(prepare)、实施(implement)、善后(windup)和汇报(report)。
【代码8-1】
这样,就可以基于该模板完成一些具体的打扫卫生工作。例如,代码8-2 和代码8-3分别给出了擦玻璃和擦黑板的实现。
【代码8-2】
【代码8-3】
可以看到,虽然具体的行为不同,但是 WipeGlass和 WipeBlackboard两者遵循的模板是一样的,都是由 Cleaning类给定的。这就是模板模式的作用,即确定了一套操作的框架,而子类只需在此框架的基础上定义具体的实现即可。
类型处理器
类型处理器的基类与实现类
作为一个 ORM框架,处理 Java对象和数据库关系之间的映射是 MyBatis工作中的重要部分。然而在 Java中存在 Integer、String、Data等各种类型的数据,在数据库中也存在varchar、longvarchar、tinyint等各种类型的数据。不同类型的字段所需的读、写方法各不相同,因此需要对不同类型的字段采取相应的处理方式。
例如,User中有如代码8-4所示的几个属性。
【代码8-4】
在对 User对象的属性进行读取和赋值时,需要用 Integer的相关处理方式来操作 id、sex属性,而要用 String的相关处理方式来操作name、schoolName属性。
在 type包中,将每种类型对应的处理方式封装在了对应的类型处理器 TypeHandler中。例如,IntegerTypeHandler负责完成对 Integer类型的处理。
type 包共有 43 个类型处理器,这些类型处理器的名称也均以“TypeHandler”结尾。而 TypeHandler和 BaseTypeHandler则分别是类型处理器接口和类型处理器基类。
图8-1展示了type包中类型处理器相关的类图。
图8-1 类型处理器类图
TypeHandler<T>是一个接口,其中定义了进行数据处理操作的几个抽象方法。而BaseTypeHandler<T>继承了 TypeHandler<T>接口,并实现了 TypeHandler<T>中的接口。
在类型处理器相关类的设计中采用了模板模式,BaseTypeHandler<T>作为所有类型处理器的基类,定义了模板的框架。而在各个具体的实现类中,则实现了具体的细节。
以代码8-5展示的 BaseTypeHandler中 getResult(ResultSet,String)方法为例,该方法完成了异常处理等统一的工作,而与具体类型相关的 getNullableResult(ResultSet,String)操作则通过抽象方法交给具体的类型处理器实现。这就是典型的模板模式。
【代码8-5】
BaseTypeHandler<T>交给具体的类型处理器实现的抽象方法一共只有四个。在每个类型处理器都需要实现这四个方法。· void setNonNullParameter(PreparedStatement,int,T,JdbcType):向 PreparedStatement对象中的指定变量位置写入一个不为null的值;
· T getNullableResult(ResultSet,String):从 ResultSet中按照字段名读出一个可能为null的数据;
· T getNullableResult(ResultSet,int):从 ResultSet 中按照字段编号读出一个可能为null的数据;
· T getNullableResult(CallableStatement,int):从 CallableStatement中按照字段编号读出一个可能为 null的数据。
因为上面的抽象方法跟具体的类型相关,因此存在泛型参数 T。在每种类型处理器中,都给出了泛型参数的值。以 IntegerTypeHandler为例,它设置泛型参数值为 Integer。IntegerTypeHandler类的源码如代码8-6所示。
【代码8-6】
代码8-6中,IntegerTypeHandler处理的类型是 Integer,这表明其 getNullableResult方法给出的就是一个 Integer结果。
TypeReference类
43个类型处理器可以处理不同 Java类型的数据,而这些类型处理器都是 TypeHandler接口的子类,因此可以都作为 TypeHandler来使用。
那会不会遇到一个问题,当 MyBatis取到某一个 TypeHandler时,却不知道它到底是用来处理哪一种 Java类型的处理器?
为了解决这一问题,MyBatis 定义了一个 TypeReference 类。它能够判断出一个TypeHandler用来处理的目标类型。而它判断的方法也很简单:取出 TypeHandler实现类中的泛型参数 T的类型,这个值的类型也便是该 TypeHandler能处理的目标类型。该功能由getSuperclassTypeParameter方法实现,该方法能将找出的目标类型存入类中的 rawType属性。getSuperclassTypeParameter方法带注释的源码如代码8-7所示。
【代码8-7】
TypeReference 类是 BaseTypeHandler 的父类,因此所有的类型处理器都继承了TypeReference 的功能。这意味着对任何一个类型处理器调用 getSuperclassTypeParameter方法,都可以得到该处理器用来处理的目标类型。
类型注册表
定义了大量的类型处理器之后,MyBatis 还需要在遇到某种类型的数据时,快速找到与数据的类型对应的类型处理器。这个过程就需要各种类型注册表的帮助。
type 包中的类型注册表有三个:SimpleTypeRegistry、TypeAliasRegistry 和TypeHandlerRegistry。
SimpleTypeRegistry 是一个非常简单的注册表,其内部使用一个SIMPLE_TYPE_SET变量维护所有 Java基本类型。SIMPLE_TYPE_SET中的赋值是在 static代码块中进行的,如代码8-8所示。这说明在 SimpleTypeRegistry初始化结束后,就已经将所有的 Java基本类型维护到了 SIMPLE_TYPE_SET中。
【代码8-8】
TypeAliasRegistry是一个类型别名注册表,其内部使用 typeAliases变量维护类型的别名与类型的对应关系。有了这个注册表,我们就可以在很多场合使用类型的别名来指代具体的类型。TypeHandlerRegistry 是这三个注册表中最为核心的一个,数据类型和相关处理器的对应关系就是由它维护的。
在介绍它之前,我们先介绍 Java数据类型和 JDBC数据类型。假设某个对象中存在一个“String name”属性,则 name属性在 Java中的数据类型是String。
然而它在数据库中可能是 char、varchar,也可能是 tinytext、text 等多种类型。因此,Java数据类型和 JDBC数据类型并不是一对一的关系,而是一对多的关系。了解到这一点之后,我们在代码8-9中直接给出 TypeHandlerRegistry类的带注释的属性。
【代码8-9】
了解了 TypeHandlerRegistry的属性后,也可以猜测出如何才能拿到一个类型的类型处理器,实际就是一个两次映射的过程。
· 根据传入的 Java 类型,调用 getJdbcHandlerMap 子方法找寻对应的jdbcTypeHandlerMap后返回。· 基于 jdbcTypeHandlerMap,根据 JDBC类型找到对应的 TypeHandler。
例如,在给定 Java类型是 String,而 JDBC类型是 varchar后,就能唯一确定一个类型处理器。getTypeHandler 方法完成的就是这一过程,该方法带注释的源码如代码8-10所示。
【代码8-10】
SimpleTypeRegistry、TypeAliasRegistry、TypeHandlerRegistry这三个类型注册表的存在,使 MyBatis 不仅可以根据类型找寻其类型处理器,而且还可以根据类型别名找寻对应的类型处理器。
本文给大家讲解的内容是通用源码阅读指导mybatis源码详解: type包
- 下篇文章给大家讲解的是通用源码阅读指导mybatis源码详解: io包;
- 觉得文章不错的朋友可以转发此文关注小编;
- 感谢大家的支持