MyBatis不用@Param也能传递多个参数?


MyBatis不用@Param也能传递多个参数?

背景

一次偶尔发现了工作的项目中有人写的代码Dao层接口传递多个参数没有用@param注解,xml文件中直接通过参数名竟然可以拿到参数值,示例如下(公司代码不便透露写了个测试用例):

Dao层接口
public  interface UserMapper {
​
​
    /**
     * 添加用户
     * @param username
     * @param password
     * @return
     */
    int insertUser(String username,String password);
}
xml
 <insert id="insertUser" >
        insert into user
            (username,password)
            values
            (#{username},#{password})
    </insert>

 

初步研究

因为在博主眼里平时传递多个参数时都会加上@Param注解的,这样xml获取参数时才能对应上,发现这个现象后开始在网上寻求答案,大体上都是说jdk1.8之后提供了Parameter反射类可以通过其getName()方法获取到。于是博主自己写了个测试类进行探究:

测试代码
/**
 * @author blue
 */
public class TestUtil {
​
    public void test(String aaa,String bbb){
​
    }
​
    public static void main(String[] args) throws NoSuchMethodException {
        //获取TestUtil的class对象
        Class c = TestUtil.class;
        //获取TestUtil的test方法
        Method method = c.getMethod("test", String.class, String.class);
        //获取test方法中的参数
        Parameter[] parameters = method.getParameters();
        for (Parameter parameter : parameters) {
            System.out.println(parameter.getName());
        }
    }
}

 

 

运行结果

image-20210527170634634

 

进一步研究(源码解析)

网上找到的答案对这个问题只有一个大概的解析,其中真正的原理还需要自己一步一步解析源码才能了解清楚,于是博主又开始通过@Param一步一步找到其中的原理。

1.通过Param接口找到使用它的类(根据类的名字很容易猜到是ParamNameResolver这个类来获取参数的)

image-20210527170522863

2.发现ParamNameResolver的构造方法有使用到Param,大体上分析一下,先判断方法上是否有@Param注解,如果有直接获取其value值作为语句参数名称,

否则就通过getActualParamName方法去获取参数。具体看下面代码的注解

  
public ParamNameResolver(Configuration config, Method method) {
    final Class<?>[] paramTypes = method.getParameterTypes();
    final Annotation[][] paramAnnotations = method.getParameterAnnotations();
    final SortedMap<Integer, String> map = new TreeMap<Integer, String>();
    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;
      for (Annotation annotation : paramAnnotations[paramIndex]) {
         // 如果方法名有Param注解,直接获取注解上value作为参数值作为语句参数名称(也就是)
        if (annotation instanceof Param) {
          hasParamAnnotation = true;
          name = ((Param) annotation).value();
          break;
        }
      }
       //如果没有获取到Param注解上面的value值
      if (name == null) {
        // @Param was not specified.
          //isUseActualParamName:mybatis的全局参数,是否允许使用方法签名中的名称(也就是方法的参数名称)作为语句参数名称
          //mybaties3.4.1版本开始默认为TRUE
        if (config.isUseActualParamName()) {
            //获取参数名...(接着往下看)
          name = getActualParamName(method, paramIndex);
        }
          //如果获取不到就设置成"0","1",...
        if (name == null) {
          // use the parameter index as the name ("0", "1", ...)
          // gcode issue #71
          name = String.valueOf(map.size());
        }
      }
      map.put(paramIndex, name);
    }
    names = Collections.unmodifiableSortedMap(map);
  }

 

3.进入getActualParamName方法中 判断了当前jdk版本中是否有Parameter类

 private String getActualParamName(Method method, int paramIndex) {
  //判断jdk版本是否存在Parameter类(具体是通过 Resources.classForName("java.lang.reflect.Parameter")判断,抛异常了就不存在,感兴趣的可以自己点进去看下源码)
    if (Jdk.parameterExists) {
      return ParamNameUtil.getParamNames(method).get(paramIndex);
    }
    return null;
  }

 

4.进入到ParamNameUtil.getParamNames()终于找到了Parameter类,先通过method获取Parameter数组,然后循环遍历将参数名存入到list中。

@UsesJava8
public class ParamNameUtil {
  public static List<String> getParamNames(Method method) {
    return getParameterNames(method);
  }
​
  public static List<String> getParamNames(Constructor<?> constructor) {
    return getParameterNames(constructor);
  }
​
    //Executable是Method和Constructor的公共父类
  private static List<String> getParameterNames(Executable executable) {
    final List<String> names = new ArrayList<String>();
    //获取到方法中的参数数组
    final Parameter[] params = executable.getParameters();
    for (Parameter param : params) {
      names.add(param.getName());
    }
    return names;
  }
​
  private ParamNameUtil() {
    super();
  }
}

 

总结

通过对mybaties@Param注解相关源码进行分析,知道了没有添加@Param注解时会通过Parameter反射类去获取方法的参数名,众所周知java反射机制效率很低,所以在jdk1.8及以上版本项目中如果没有设置Mybatie的isUseActualParamName=false

在传递参数时尽量使用@Param注解

 

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM