You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
source-code-hunter/docs/Mybatis/核心处理层/Mybatis-ParamNameResolver.md

209 lines
7.0 KiB

# ParamNameResolver 源码解析
5 years ago
- Author: [HuiFer](https://github.com/huifer)
5 years ago
- Description: 该文介绍 mybatis `@Param` 注解和`ParamNameResolver`
- 源码阅读工程: [huifer-mybatis](https://github.com/huifer/javaBook-src/tree/old/mybatis-3)
5 years ago
## 源码
- `org.apache.ibatis.reflection.ParamNameResolver`
```java
/**
* {@link Param} 注解的扫描工具和处理工具
*/
public class ParamNameResolver {
public static final String GENERIC_NAME_PREFIX = "param";
/**
* <p>
* The key is the index and the value is the name of the parameter.<br />
* The name is obtained from {@link Param} if specified. When {@link Param} is not specified,
* the parameter index is used. Note that this index could be different from the actual index
* when the method has special parameters (i.e. {@link RowBounds} or {@link ResultHandler}).
* </p>
* <ul>
* <li>aMethod(@Param("M") int a, @Param("N") int b) -&gt; {{0, "M"}, {1, "N"}}</li>
* <li>aMethod(int a, int b) -&gt; {{0, "0"}, {1, "1"}}</li>
* <li>aMethod(int a, RowBounds rb, int b) -&gt; {{0, "0"}, {2, "1"}}</li>
* </ul>
*
* {@link ParamNameResolver#ParamNameResolver(org.apache.ibatis.session.Configuration, java.lang.reflect.Method)} 中的map 变量值转换而得
* {参数索引: 参数名称(arg0,Param注解的value)}
*
*/
private final SortedMap<Integer, String> names;
private boolean hasParamAnnotation;
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;
// get names from @Param annotations
for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
if (isSpecialParameter(paramTypes[paramIndex])) {
// skip special parameters
// 如果是特殊类型跳过
continue;
}
String name = null;
// 注解扫描@Param
for (Annotation annotation : paramAnnotations[paramIndex]) {
// 是否为 Param 注解的下级
if (annotation instanceof Param) {
hasParamAnnotation = true;
// 获取 value 属性值
name = ((Param) annotation).value();
break;
}
}
if (name == null) {
// 如果没有写 @param 处理方式如下
// @Param was not specified.
if (config.isUseActualParamName()) {
name = getActualParamName(method, paramIndex);
}
if (name == null) {
// use the parameter index as the name ("0", "1", ...)
// gcode issue #71
name = String.valueOf(map.size());
}
}
// 循环参数列表 放入map 对象
map.put(paramIndex, name);
}
names = Collections.unmodifiableSortedMap(map);
}
/**
* 是否为特殊参数 , 依据 是否是 {@link RowBounds} 或者 {@link ResultHandler}
* @param clazz
* @return
*/
private static boolean isSpecialParameter(Class<?> clazz) {
return RowBounds.class.isAssignableFrom(clazz) || ResultHandler.class.isAssignableFrom(clazz);
}
/**
* 返回方法名 参数索引
* @param method
* @param paramIndex
* @return
*/
private String getActualParamName(Method method, int paramIndex) {
return ParamNameUtil.getParamNames(method).get(paramIndex);
}
/**
* Returns parameter names referenced by SQL providers.
*/
public String[] getNames() {
return names.values().toArray(new String[0]);
}
/**
* <p>
* A single non-special parameter is returned without a name.
* Multiple parameters are named using the naming rule.
* In addition to the default names, this method also adds the generic names (param1, param2,
* ...).
* </p>
* <p>
* 通常参数异常在这个地方抛出 param ... 异常
* 获取参数名称,和参数传递的真实数据
*/
public Object getNamedParams(Object[] args) {
final int paramCount = names.size();
if (args == null || paramCount == 0) {
// 是否有参数
return null;
} else if (!hasParamAnnotation && paramCount == 1) {
// 没有使用 @param 注解 参数只有一个
return args[names.firstKey()];
} 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()]);
// add generic param names (param1, param2, ...)
// param + 当前索引位置
final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);
// ensure not to overwrite parameter named with @Param
if (!names.containsValue(genericParamName)) {
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}
}
```
## debug 阶段
- 测试用例为同一个 每次修改mapper方法参数来进行debug
```java
@Test
void testXmlConfigurationLoad() throws IOException {
Reader reader = Resources.getResourceAsReader("mybatis-config-demo.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
Configuration configuration = factory.getConfiguration();
SqlSession sqlSession = factory.openSession();
HsSellMapper mapper = sqlSession.getMapper(HsSellMapper.class);
List<HsSell> list = mapper.list(2);
List<Object> objects = sqlSession.selectList("com.huifer.mybatis.mapper.HsSellMapper.list");
assertEquals(list.size(), objects.size());
}
```
```java
List<HsSell> list( Integer id);
```
如果不写`@Param`称则返回
![image-20191219083223084](assets/image-20191219083223084.png)
```java
List<HsSell> list(@Param("ID") Integer id);
```
- 写`@Param`返回
![image-20191219083344439](/images/mybatis/image-20191219083344439.png)
![image-20191219083354873](/images/mybatis/image-20191219083354873.png)
- `org.apache.ibatis.reflection.ParamNameResolver#getNamedParams`
```java
List<HsSell> list( Integer id);
```
![image-20191219084455292](/images/mybatis/image-20191219084455292.png)
```java
List<HsSell> list(@Param("ID") Integer id);
```
写上`@Param`
![image-20191219084943102](/images/mybatis/image-20191219084943102.png)
![image-20191219085131167](/images/mybatis/image-20191219085131167.png)