Mybatis技术内幕读书笔记(四)binding模块(动态代理Mapper)

前言:

在iBatis ( MyBatis 的前身)中,查询一个Blog 对象时需要调用SqlSession.queryForObject(”selectBlog ” ,blogld )方法。其中, SqlSession . queryForObject ()方法会执行指定的SQL 语句进行查询井返回一个结果对象, 第一个参数“ selectBlog"指明了具体执行的SQL 语句的id ,该SQL语句定义在相应的映射配置文件中。如果我们错将“ selectBlog ” 写成了“ selectBlog1”, 在初始化过程中, MyBatis 是无法提示该错误的,而在实际调用queryForObject (” selectBiog1 ”, blogld)方法时才会抛出异常, 开发人员才能知道该错误。

MyBatis 提供了binding 模块用于解决上述问题,我们可以定义一个接口,该示例中为BlogMapper接口,具体代码如下所示。注意,这里的BlogMapper 接口并不需要继承任何其他接口,而且开发人员不需要提供该接口的实现。

MapperRegistry&MapperProxyFactory:

MapperRegistryMapper 接口及其对应的代理对象工厂的注册中心。Configuration 是MyBatis全局性的配置对象,在MyBatis 初始化的过程中,所有配置信息会被解析成相应的对象井记录到Configuration 对象中。这里关注Configuration.mapperRegisty字段,它记录当前使用的MapperRegistry 对象,MapperRegisty中字段的含义和功能如下:

在MyBatis 初始化过程中会读取映射配置文件以及Mapper 接口中的注解信息,并调用Mapper Registy.addMapper()方法填充MapperRegistry. knownMappers 集合,该集合的key 是Mapper 接口对应的Class 对象, value 为MapperProxyFactory 工厂对象,可以为Mapper 接口创建代理对象, MapperProxy Factory 的实现马上就会分析到。MapperRegistry.addMapper() 方法的
部分实现如下:

在需要执行某SQL 语句时,会先调用MapperRegistry.getMapper () 方法获取实现了Mapper接口的代理对象,实际上是MyBatis 通过JDK 动态代理为BlogMapper 接口生成的代理对象。

MapperProxyFactory 主要负责创建代理对象, 其中核心字段的含义和功能如下:

MapperProxy:

MapperProxy 实现了lnvocationHandler 接口,在介绍JDK 动态代理时己经介绍过,该接口的实现是代理对象的核心逻辑,这里不再重复描述。MapperP roxy 中核心宇段的含义和功能如下:

MapperMethod:

Mapper Method 中封装了Mapper 接口中对应方法的信息,以及对应SQL 语句的信息。读者可以将MapperMethod 看作连接Mapper 接口以及映射配置文件中定义的SQL 语句的桥梁。Mapper Method 中各个字段的信息如下:

SqlCommand:

SqlCommand 是MapperMethod 中定义的内部类,它使用name 字段记录了SQL 语句的名称,使用type 宇段( SqlCommandType 类型)记录了SQL 语句的类型。SqlCommandType 是枚举类型,有效取值为UNKNOWN 、INSERT 、UPDATE 、DELETE 、SELECT 、FLUSH 。SqlCommand 的构造方法会初始化name 字段和type 字段,代码如下:

ParamNameResolver:

在MethodSignature 中,会使用ParamNameResolver处理Mapper接口中定义的方法的参数列表。ParamNameResolver 使用name 宇段( SortedMap<Integer, String>类型)记录了参数在参数列表中的位置索引与参数名称之间的对应关系,其中key 表示参数在参数列表中的索引位置,value 表示参数名称,参数名称可以通过@Param 注解指定,如果没有指定@Param 注解,则使用参数索寻|作为其名称。如果参数列表中包含Row Bounds 类型或ResultHandler 类型的参数,则这两种类型的参数并不会被记录到name 集合中,这就会导致参数的索引与名称不一致,例如,method(int a, RowBounds rb, int b)方法对应的names 集合为{{ 0,”0”}, {2 ,” 1 ”}},如图2-39所示。

ParamNameResolver 的hasParamAnnotation 字段( boolean 类型)记录对应方法的参数列表中是否使用了@Param 注解。
在ParamNameResolver 的构造方法中,会通过反射的方式读取Mapper 接口中对应方法的信息,井初始化上述两个字段。

MethodSignature:

介绍完ParamNameReso lver 的功能,回到MethodSignature 继续介绍。MethodSignature 也是MapperMethod 中定义的内部类,其中封装了Mapper 接口中定义的方法的相关信息,MethodSignature 中核心字段的含义如下:

MapperMethod.execute():

回到MapperMethod 继续分析。MapperMethod 中最核心的方法是execute()方法,它会根据SQL 语句的类型调用SqISession 对应的方法完成数据库操作。SqlSession 是MyBatis 的核心组件之一,MapperMethod .execute()方法的具体实现如下:

总结:

Configuation里面保存着MapperRegisty,里面保留着key是接口class为key,MapperProxyFactory为value的map,MapperProxyFactory里面会创建一个实现了InvocationHandler的MapperProxy对象,然后使用它来进行jdk动态代理返回对象。

invoke方法会调用MapperProxy的execute方法,MapperProxy里面保留着一个以Method为key,MapperMethod为value的缓存map,MapperMethod里面包含着sqlCommand和type,sqlCommend会根据接口名方法名组合去获取MapppedStatement对象,然后执行sql语句获得resultset,最后交给resultsethandler来处理返回结果。

展开阅读全文

没有更多推荐了,返回首页