通用源码阅读指导mybatis源码详解:type包

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包

  1. 下篇文章给大家讲解的是通用源码阅读指导mybatis源码详解: io包;
  2. 觉得文章不错的朋友可以转发此文关注小编;
  3. 感谢大家的支持