一直都比较好奇Mybatis的参数名解析(@Param) 是怎么做的。虽然大致也能猜到是反射拿到注解信息。
今天从debug源码出发,开始分析是哪些类完成的命名解析。
1.@Param mybatis支持使用@param 来为Mapper方法的参数起别名:
1 2 3 4 5 List<TCtInfoManagerInfo> getTCtInfoManagerInfoByProjectSubId (@Param("id") String projectSubId, @Param("foo") String foo, @Param("foo1") String foo1) ;
并在xml中进行调用:
1 2 3 4 5 6 7 <select id ="getTCtInfoManagerInfoByProjectSubId" resultType ="com.bjdv.es.web.service.dto.TCtInfoManagerInfo" > SELECT * FROM `project_sub_team` A LEFT JOIN `t_ct_info` B ON A.info_id = B.id LEFT JOIN `sys_user` C ON C.id = B.manager WHERE A.sub_id = #{id} and A.foo = #{foo} and A.foo1 = {foo1}</select >
我们称这种为 参数名(ParamName)。
事实上每个参数都有一个默认名,我们可以这样使用:
1 2 3 4 5 List<TCtInfoManagerInfo> getTCtInfoManagerInfoByProjectSubId (String projectSubId, String foo, String foo1) ;
1 2 3 4 5 6 7 <select id ="getTCtInfoManagerInfoByProjectSubId" resultType ="com.bjdv.es.web.service.dto.TCtInfoManagerInfo" > SELECT * FROM `project_sub_team` A LEFT JOIN `t_ct_info` B ON A.info_id = B.id LEFT JOIN `sys_user` C ON C.id = B.manager WHERE A.sub_id = #{param1} and A.foo = #{param2} and A.foo1 = {param3}</select >
mybatis在解析命名的时候,为没有命名的参数,存入了默认命名。它取决于方法中参数的顺序,依次为:
param1 param2 param3…
并且,这个名称是冗余的名称,即使定义了 param ,我们也可以使用。
2.参数名是如何解析的? Mybatis通过org.apache.ibatis.reflection.ParamNameResolver完成参数解析的。
名称规则是:
如果参数使用 Param(value) 修饰,那么参数的名称为value 。
如果没有使用参数, 则使用参数的索引
对于特殊参数(RowBounds 或 ResultHandler ),不会算作param (也就是无法通过 param+index 来引用他们)
例如:
1 2 3 public foo (@Param("M") int a, @Param("N") int b) ; public foo (int a, int b) public foo (int a, RowBounds rb, int b)
每一个MapperMethod ,都会创建一个参数名解析器(ParamNameResolver),在它的构造器中,完成了
“ 参数在方法中的索引-> 参数名”的解析:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public ParamNameResolver (Configuration config, Method method) { this .useActualParamName = config.isUseActualParamName(); 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) { hasParamAnnotation = true ; name = ((Param) annotation).value(); break ; } } if (name == null ) { if (useActualParamName) { name = getActualParamName(method, paramIndex); } if (name == null ) { name = String.valueOf(map.size()); } } map.put(paramIndex, name); } names = Collections.unmodifiableSortedMap(map); }
随后,在获取参数名的时候,会使用 GENERIC_NAME_PREFIX 静态变量最为前缀,拼接索引+1 。
而GENERIC_NAME_PREFIX 的默认值为 param ,由此出现了默认名 param1 param2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public Object getNamedParams (Object[] args) { final int paramCount = names.size(); if (args == null || paramCount == 0 ) { return null ; } else if (!hasParamAnnotation && paramCount == 1 ) { Object value = args[names.firstKey()]; return wrapToMapIfCollection(value, useActualParamName ? names.get(0 ) : null ); } else { final Map<String, Object> param = new ParamMap <>(); int i = 0 ; for (Map.Entry<Integer, String> entry : names.entrySet()) { param.put(entry.getValue(), args[entry.getKey()]); final String genericParamName = GENERIC_NAME_PREFIX + (i + 1 ); if (!names.containsValue(genericParamName)) { param.put(genericParamName, args[entry.getKey()]); } i++; } return param; } }
并且,这个方法将 List 、Array的参数都转换为了Map