通过源码分析MyBatis中Param注解的定义与解析过程
保留所有版权,请引用而不是转载本文(原文地址 https://yeecode.top/blog/51/ )。
前面的文章中,我们已经对Java注解的创建、使用进行了介绍。接下来,我们将介绍如何在程序中解析注解,从而获取注解中承载的信息。
我们以MyBatis中最常用的Param
注解为例,介绍下它如何被解析然后发挥作用。(均参考自书籍《通用源码阅读指导书——MyBatis源码详解》)
Param
注解带注释的源码如下面代码所示。
@Documented // 表明该注解会保留在API文档中
@Retention(RetentionPolicy.RUNTIME) // 表明注解会保留到运行阶段
@Target(ElementType.PARAMETER) // 表明注解可以应用在参数上
public @interface Param {
String value(); // 整个注解只有一个属性,名为value
}
使用时,我们只需要在相关参数上使用该注解对参数进行命名,便可以在映射文件中引用该参数。例如下面的示例中,我们使用该注解将userId
属性命名为id
。
User queryById(@Param("id") Integer userId);
这样,我们便可以在Mapper引用id
所指代的变量,如下所示。
<select id="queryById" resultType="User">
SELECT *
FROM user
WHERE `id` = #{id}
</select>
接下来我们着重分析该注解是如何生效的。
借助于开发工具,我们在ParamNameResolver
的构造方法中找到了MyBatis对引Param
的引用。相关源码如下所示。
/**
* 参数名解析器的构造方法
* @param config 配置信息
* @param method 要被分析的方法
*/
public ParamNameResolver(Configuration config, Method method) {
// 获取参数类型列表
final Class<?>[] paramTypes = method.getParameterTypes();
// 准备存取所有参数的注解,是二维数组
final Annotation[][] paramAnnotations = method.getParameterAnnotations();
final SortedMap<Integer, String> map = new TreeMap<>();
int paramCount = paramAnnotations.length;
// 循环处理各个参数
for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
if (isSpecialParameter(paramTypes[paramIndex])) {
// 跳过特别的参数
continue;
}
// 参数名称
String name = null;
for (Annotation annotation : paramAnnotations[paramIndex]) {
// 找出参数的注解
if (annotation instanceof Param) {
// 如果注解是Param
hasParamAnnotation = true;
// 那就以Param中值作为参数名
name = ((Param) annotation).value();
break;
}
}
if (name == null) {
// 否则,保留参数的原有名称
if (config.isUseActualParamName()) {
name = getActualParamName(method, paramIndex);
}
if (name == null) {
// 参数名称取不到,则按照参数index命名
name = String.valueOf(map.size());
}
}
map.put(paramIndex, name);
}
names = Collections.unmodifiableSortedMap(map);
}
在上述代码中,首先使用语句“final Annotation[][] paramAnnotations = method.getParameterAnnotations()
”得到了目标方法的所有参数的注解。假设我们定义如下面代码所示的方法:
User queryById(@Param("id") Integer userId, String userName, @NonNull @Param("sName") String schoolName);
则会在paramAnnotations得到如图所示的结果:
然后在分析到每个参数时,循环遍历它的每个注解并使用“annotation instanceof Param
”判断当前注解是否为“Param”注解。如果当前注解为“Param
”注解,则会使用“((Param) annotation).value()
”操作获取该注解的value值作为参数的名称。
这样,我们对Integer userId
参数使用了@Param("id")
后,“id”便成了实参的名称,因此我们能够使用“id”索引到对应的实参。
可见,阅读源码对我们理解编程知识的使用十分有用。推荐大家阅读《通用源码阅读指导书——MyBatis源码详解》。
《通用源码阅读指导书——MyBatis源码详解》是一本以MyBatis的源码为实例讲述源码阅读方法的书籍,并且附带有示例项目源码,MyBatis的全中文注解。书籍还总结了大量的编程知识和架构经验,对提升编程和架构能力十分有用,非常推荐。
可以访问个人知乎阅读更多文章:易哥(https://www.zhihu.com/people/yeecode),欢迎关注。