标签:bin nal cti 动态代理 span ring statement str 存在
本文,我们来分享 MyBatis 的 Binding 模块,对应 binding
包。如下图所示:binding
包
在 《精尽 MyBatis 源码解析 —— 项目结构一览》 中,简单介绍了这个模块如下:
在调用 SqlSession 相应方法执行数据库操作时,需要指定映射文件中定义的 SQL 节点,如果出现拼写错误,我们只能在运行时才能发现相应的异常。为了尽早发现这种错误,MyBatis 通过 Binding 模块,将用户自定义的 Mapper 接口与映射配置文件关联起来,系统可以通过调用自定义 Mapper 接口中的方法执行相应的 SQL 语句完成数据库操作,从而避免上述问题。
值得读者注意的是,开发人员无须编写自定义 Mapper 接口的实现,MyBatis 会自动为其创建动态代理对象。在有些场景中,自定义 Mapper 接口可以完全代替映射配置文件,但有的映射规则和 SQL 语句的定义还是写在映射配置文件中比较方便,例如动态 SQL 语句的定义。
本文涉及的类如下图所示:类图
org.apache.ibatis.binding.MapperRegistry
,Mapper 注册表。
// MapperRegistry.java
|
#addMappers(String packageName, ...)
方法,扫描指定包,并将符合的类,添加到 knownMappers
中。代码如下:
// MapperRegistry.java
|
<1>
处,使用 ResolverUtil 扫描指定包下的指定类。详细解析,在 《精尽 MyBatis 源码分析 —— IO 模块》 中有。#hasMapper(Class<T> type)
方法,判断是否有 Mapper 。代码如下:
// MapperRegistry.java
|
#addMapper(Class<T> type)
方法,添加到 knownMappers
中。代码如下:
// MapperRegistry.java
|
<1>
处,判断 type
必须是接口,也就是说 Mapper 接口。<2>
处,已经添加过,则抛出 BindingException 异常。<3>
处,添加到 knownMappers
中。<4>
处,解析 Mapper 的注解配置。<5>
处,创建 MapperAnnotationBuilder 对象,解析 Mapper 的注解配置。<6>
处,若加载未完成,从 knownMappers
中移除。#getMapper(Class<T> type, SqlSession sqlSession)
方法,获得 Mapper Proxy 对象。代码如下:
// MapperRegistry.java
|
<1>
处,从 knownMappers
中,获得 MapperProxyFactory 对象。<2>
处,调用 MapperProxyFactory#newInstance(SqlSession sqlSession)
方法,创建 Mapper Proxy 对象。详细解析,见 「3. MapperProxyFactory」 。org.apache.ibatis.binding.MapperProxyFactory
,Mapper Proxy 工厂类。
// MapperProxyFactory.java
|
#newInstance(...)
方法,创建 Mapper Proxy 对象。代码如下:
// MapperProxyFactory.java
|
org.apache.ibatis.binding.MapperProxy
,实现 InvocationHandler、Serializable 接口,Mapper Proxy 。关键是 java.lang.reflect.InvocationHandler
接口,你懂的。
// MapperProxy.java
|
#invoke(Object proxy, Method method, Object[] args)
方法,调用方法。代码如下:
// MapperProxy.java
|
<1>
处,如果是 Object 定义的方法,直接调用。<2>
处,调用 #isDefaultMethod((Method method)
方法,判断是否为 default
修饰的方法,若是,则调用 #invokeDefaultMethod(Object proxy, Method method, Object[] args)
方法,进行反射调用。代码如下:
// MapperProxy.java
|
default
修饰符。怎么进行反射调用,见 《java8 通过反射执行接口的default方法》 一文。<3.1>
处,调用 #cachedMapperMethod(Method method)
方法,获得 MapperMethod 对象。代码如下:
// MapperProxy.java
|
methodCache
缓存中获取。如果不存在,则进行创建,并进行缓存。<3.2>
处,调用 MapperMethod#execute(SqlSession sqlSession, Object[] args)
方法,执行 MapperMethod 方法。关于 MapperMethod ,在 「5. MapperMethod」 中,详细解析。org.apache.ibatis.binding.MapperMethod
,Mapper 方法。在 Mapper 接口中,每个定义的方法,对应一个 MapperMethod 对象。
// MapperMethod.java
|
command
属性,SqlCommand 对象。关于它的详细解析,见 「6. SqlCommand」 。method
属性,MethodSignature 对象。关于它的详细解析,见 「7. MethodSignature」 。因为涉及比较多的后面的内容,所以放在 详细解析。
心急的胖友,可以先看看 《Mybatis3.3.x技术内幕(十一):执行一个Sql命令的完整流程》 。
SqlCommand ,是 MapperMethod 的内部静态类,SQL 命令。
// SqlCommand.java
|
name
属性,对应 MappedStatement#getId()
方法获得的标识。实际上,就是 ${NAMESPACE_NAME}.${语句_ID}
, 例如:"org.apache.ibatis.autoconstructor.AutoConstructorMapper.getSubject2"
。type
属性,SQL 命令类型。org.apache.ibatis.mapping.SqlCommandType
类,代码如下:
// SqlCommandType.java
|
<1>
处,调用 #resolveMappedStatement(Class<?> mapperInterface, String methodName, Class<?> declaringClass, Configuration configuration)
方法,获得 MappedStatement 对象。详细解析,胖友先跳到 「6.2 resolveMappedStatement」 。<2>
处,如果找不到 MappedStatement 对象,说明该方法上,没有对应的 SQL 声明。那么在判断是否有 @Flush
注解,如果有,说明该方法是用于执行 flush 操作,否则,抛出 BindingException 异常。<3>
处,如果找到 MappedStatement 对象,则初始化 name
和 type
属性。#resolveMappedStatement(Class<?> mapperInterface, String methodName, Class<?> declaringClass, Configuration configuration)
方法,获得 MappedStatement 对象。代码如下:
// SqlCommand.java
|
<1>
处,获得编号。这个编号,和我们上文提到的 ${NAMESPACE_NAME}.${语句_ID}
。<2>
处,通过 Configuration#hasStatement(String statementId)
方法,判断是否有 MappedStatement 。如果有,则调用 Configuration#getMappedStatement(String statementId)
方法,获得 MappedStatement 对象。关于 Configuration ,我们在后续的文章中,详细解析。在这里,胖友只需要知道,Configuration 里缓存了所有的 MappedStatement ,并且每一个 XML 里声明的例如 <select />
或者 <update />
等等,都对应一个 MappedStatement 对象。<3>
处,如果没有,并且当前方法就是 declaringClass
声明的,则说明真的找不到。<4>
处,遍历父接口,递归继续获得 MappedStatement 对象。因为,该该方法定义在父接口中。MethodSignature ,是 MapperMethod 的内部静态类,方法签名。
// MethodSignature.java
|
<1>
处,调用 #getMapKey(Method method)
方法,获得注解的 {@link MapKey#value()}
。代码如下:
// MethodSignature.java
|
<2>
处,调用 #getUniqueParamIndex(Method method, Class<?> paramType)
方法,获得指定参数类型在方法参数中的位置。代码如下:
// MethodSignature.java
|
#convertArgsToSqlCommandParam(Object[] args)
方法,获得 SQL 通用参数。代码如下:
// MethodSignature.java
|
ParamNameResolver#getNamedParams(Object[] args)
方法。在 《精尽 MyBatis 源码分析 —— 反射模块》 中,有详细解析。args
和转换后的结果的示例:示例标签:bin nal cti 动态代理 span ring statement str 存在
原文地址:https://www.cnblogs.com/siye1989/p/11622041.html