Mybatis参数解析分析

Mybatis参数解析分析

这个例子很简单,日常调用接口的时候,传递的参数,是被Mybatis怎么来解析的,为什么在写XML中,idea中会有提示,param1,param2。有的时候还有默认的参数,现在就来分析分析

例子就不举了,太简单了。直接开冲

组装参数

看过之前文章的朋友,肯定会很熟悉下面的代码。

在说一个小tips,在分析组装参数的时候,要盯着args。因为在反射调用的时候,会将args传递过来,所以,主要盯着args,肯定能找到对应的代码

 // 这是返回值为Map的查询。别的基本都一样。
  private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) {
    Map<K, V> result;
    // 这里做转化。这个method就是 `MethodSignature`,所以,直接看这里面写了什么东西就好了
    Object param = method.convertArgsToSqlCommandParam(args);
    if (method.hasRowBounds()) {
      RowBounds rowBounds = method.extractRowBounds(args);
      result = sqlSession.selectMap(command.getName(), param, method.getMapKey(), rowBounds);
    } else {
      result = sqlSession.selectMap(command.getName(), param, method.getMapKey());
    }
    return result;
  }

MethodSignature.convertArgsToSqlCommandParam(args)

  public Object convertArgsToSqlCommandParam(Object[] args) {
      return paramNameResolver.getNamedParams(args);
    }

哎呀,上面的代码平平无奇,调用了ParamNameResolver的方法,直接看

ParamNameResolver.getNamedParams

这里的代码不难,看看就能通,注意注释

先别看这个,看完下面的,再看这个

public ParamNameResolver(Configuration config, Method method) {
  // 这个值默认就是true,
    this.useActualParamName = config.isUseActualParamName();
  // 拿到参数类型的数组
    final Class<?>[] paramTypes = method.getParameterTypes();
  // 拿到参数的注解
    final Annotation[][] paramAnnotations = method.getParameterAnnotations();
  
  //treeMap用于排序
    final SortedMap<Integer, String> map = new TreeMap<>();
  //数量
    int paramCount = paramAnnotations.length;
     //解析@param注解。开始遍历了
    for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
      //判断方法的参数是否有 RowBounds和ResultHandler,跳过这些
      if (isSpecialParameter(paramTypes[paramIndex])) {
        // skip special parameters
        continue;
      }
      String name = null;
      // 遍历注解列表,如果有一个是param的。赋属性,和获取值给name。,注意,这里比较骚的是paramAnnotations是一个二维数组。
      for (Annotation annotation : paramAnnotations[paramIndex]) {
        if (annotation instanceof Param) {
          hasParamAnnotation = true;
          name = ((Param) annotation).value();
          break;
        }
      }
      //没找到名字
      if (name == null) {
        // 这个参数的意思是,是否要实际的参数名字
        if (useActualParamName) {
          //这还不是字段的名字,这是这上面注解的值,这是JDK的方法, 之后我看看这里干了什么事情
          name = getActualParamName(method, paramIndex);
        }
        // 要还是没有的话,
        if (name == null) {
         //就直接给一个String.valueOf(map.size());
          name = String.valueOf(map.size());
        }
      }
      // 所以这里的map也就是Names,是用treemap来做排序的,并且name如果是@Param的话,就是他,没有的话就是arg0,arg1......
      map.put(paramIndex, name);
    }
    names = Collections.unmodifiableSortedMap(map);
  }



 private String getActualParamName(Method method, int paramIndex) {
    return ParamNameUtil.getParamNames(method).get(paramIndex);
  }

看完上面的代码,已经知道了names里面的值是怎么来的了,继续看下面的。

看这第一行的代码发现有一个names,这个names是在MethodSignature创建的时候创建的。所以,先看看他是怎么创建的,主要干了什么事情。关于MethodSignature看上面的代码。

public Object getNamedParams(Object[] args) {
  //这个names在哪里用的呢?还是我们的老办法,直接点开看,看看哪里能引用他。最后找到了在创建MethodSignature的时候会创建ParamNameResolver,并且解析Param注解。看上面的代码分析
    final int paramCount = names.size();
  
  // 如果两个中都没有,就说明,这个查询没有参数。方法上没有参数。其实我感觉args校验没有啥意思,如果说方法上有参数,但是这里方法调用的时候依然没有,那也idea的编译都过不了。
    if (args == null || paramCount == 0) {
      return null;
      
      //如果说没有param的注解,并且参数的数量为一个,就会对于collection或者array类型的增加默认值。
    } else if (!hasParamAnnotation && paramCount == 1) { 
      Object value = args[names.firstKey()]; 
      return wrapToMapIfCollection(value, useActualParamName ? names.get(0) : null);
    } else {
      
      // 否则,就遍历Names,注意Names是Treemap,他的遍历是中序遍历。如果key是数字的话, 那就是1,2,3,4,5
      // 
      final Map<String, Object> param = new ParamMap<>();
      int i = 0;
      for (Map.Entry<Integer, String> entry : names.entrySet()) {
        // 将之前的的值放进去,value就是注解的值,或者arg1,arg2,value就是真正的值
        param.put(entry.getValue(), args[entry.getKey()]);
        
        // 同时也会添加以 param开头的key
        //   public static final String GENERIC_NAME_PREFIX = "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;
    }
  }
 

//
 //如果是collection的话,name就有一个collection,如果是list就是list,如果是array就是array。
  public static Object wrapToMapIfCollection(Object object, String actualParamName) {
    if (object instanceof Collection) {
      ParamMap<Object> map = new ParamMap<>();
      map.put("collection", object);
      if (object instanceof List) {
        map.put("list", object);
      }
      Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
      return map;
    } else if (object != null && object.getClass().isArray()) {
      ParamMap<Object> map = new ParamMap<>();
      map.put("array", object);
      Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
      return map;
    }
    return object;
  }

总结,分为两个阶段,一个节点是解析Param,一个阶段是通过Param组装Map

  1. 在创建ParamNameResolver的时候,会解析方法上的@param,如果没有的话,就调用getActualParamName方法(值就是arg1,arg2)获取名字。并且方法参数的下标为key,名字为value,放在treeMap中。

  2. 组装参数,如果当前的方法没有被@param注解,并且参数的值为一个,如果是collection的话,name就有一个collection,如果是list就是list,如果是array就是array。如果不是。就会按照上面组装的那样,key是treeMap中的value,value就是实际的值,并且会放置param开头的参数,就会通过上面组装TreeMap遍历时候通过下标获取值,组装map。返回

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值