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