【Mybatis源码分析 9】@Param命名参数源码详解

【Mybatis源码分析 8】mybatis的mapper接口中dao方法并没有具体实现类,是怎么通过mapper动态代理运行的中讲到mybatis使用mapper接口方法,通过MapperProxy动态代理去执行MapperMethod的execute方法,从而执行CRUD操作。

MapperMethod对象创建的时候,初始化它的MethodSignature子类的实例成员时,会实例化一个ParamNameResolver参数名解析器,作用时将接口方法的注解描述的sql语句中的参数名解析为接口方法入参名(尤其是对@Param注解的映射)

//MapperMethod
    public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
        this.command = new MapperMethod.SqlCommand(config, mapperInterface, method);
        // 初始化方法签名
        this.method = new MapperMethod.MethodSignature(config, mapperInterface, method);
    }

//MapperMethod.MethodSignature
        public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
            Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
            if (resolvedReturnType instanceof Class) {
                this.returnType = (Class)resolvedReturnType;
            } else if (resolvedReturnType instanceof ParameterizedType) {
                this.returnType = (Class)((ParameterizedType)resolvedReturnType).getRawType();
            } else {
                this.returnType = method.getReturnType();
            }

            this.returnsVoid = Void.TYPE.equals(this.returnType);
            this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
            this.returnsCursor = Cursor.class.equals(this.returnType);
            this.returnsOptional = Optional.class.equals(this.returnType);
            this.mapKey = this.getMapKey(method);
            this.returnsMap = this.mapKey != null;
            this.rowBoundsIndex = this.getUniqueParamIndex(method, RowBounds.class);
            this.resultHandlerIndex = this.getUniqueParamIndex(method, ResultHandler.class);

            // 初始化参数解析器
            this.paramNameResolver = new ParamNameResolver(configuration, method);
        }

在初始化ParamNameResolver时,看一下它的构造函数做了什么

public class ParamNameResolver {
    public static final String GENERIC_NAME_PREFIX = "param";
    private final SortedMap<Integer, String> names;  // 保存sql语句中的参数(key为参数在sql语句中的顺序,String为参数名)
    private boolean hasParamAnnotation;  //标识是否使用了@Param注解

    //构造方法
    public ParamNameResolver(Configuration config, Method method) {
        // 获取接口方法的所有入参类型
        Class<?>[] paramTypes = method.getParameterTypes();
        // 获取接口方法中的所有@Param注解
        Annotation[][] paramAnnotations = method.getParameterAnnotations();
        // 创建map保存解析@Param后的参数顺序-参数名
        SortedMap<Integer, String> map = new TreeMap();

        int paramCount = paramAnnotations.length;
        for(int paramIndex = 0; paramIndex < paramCount; ++paramIndex) {
            if (!isSpecialParameter(paramTypes[paramIndex])) {
                String name = null;
                Annotation[] var9 = paramAnnotations[paramIndex];
                int var10 = var9.length;

                for(int var11 = 0; var11 < var10; ++var11) {
                    Annotation annotation = var9[var11];
                    if (annotation instanceof Param) {
                        // 注解为 Param 类型
                        this.hasParamAnnotation = true;
                        // 获取@Param注解描述的入参名
                        name = ((Param)annotation).value();
                        break;
                    }
                }

                if (name == null) {
                    if (config.isUseActualParamName()) {
                        name = this.getActualParamName(method, paramIndex);
                    }
                    if (name == null) {
                        name = String.valueOf(map.size());
                    }
                }

                // 将sql中使用@Param起的参数别名转换为接口方法入参的参数名并存储到map中
                map.put(paramIndex, name);
            }
        }

        //解析完所有的sql参数,到这里就将@Param注解的参数名都转换成接口方法入参名了,然后存储到ParamNameResolver的namas字段中。
        this.names = Collections.unmodifiableSortedMap(map);
    }

    // 其他方法
}

mybatis的mapper接口方法的动态代理执行过程,在MapperMethod执行execute方法,调用SqlSession的select等方法前都会先执行:

Object param = this.method.convertArgsToSqlCommandParam(args);

这一步是去解析sql语句中的参数转换为接口方法入参(调用方法签名的paramNameResolver的getNameParams()方法)

public Object convertArgsToSqlCommandParam(Object[] args) {
    return this.paramNameResolver.getNamedParams(args);
}
//paramNameResolver
    public Object getNamedParams(Object[] args) {
        // 获取参数个数, ParamNameReslover的成员private final SortedMap<Integer, String> names中保存sql语句中的所有参数名(按顺序存储到sortmap<integer, String>, key为String参数名在sql语句中的顺序)
        int paramCount = this.names.size();
        if (args != null && paramCount != 0) {
            if (!this.hasParamAnnotation && paramCount == 1) {
                // 如果没有使用@Param注解给参数起别名,并且只有一个参数的时候,直接返回args第一个元素(事实上args也应该只传入一个参数)
                return args[(Integer)this.names.firstKey()];
            } else {
                // 使用了@Param注解或者参数个数大于1
                Map<String, Object> param = new ParamMap();
                int i = 0;
                // 遍历sql语句中的所有参数
                for(Iterator var5 = this.names.entrySet().iterator(); var5.hasNext(); ++i) {
                    Entry<Integer, String> entry = (Entry)var5.next();
                    // 解析sql语句中的参数,映射到传入参数(args)
                    param.put((String)entry.getValue(), args[(Integer)entry.getKey()]);
                    // 可以使用"param1、param2、param3...paramN"的方式映射入参
                    String genericParamName = "param" + (i + 1);
                    if (!this.names.containsValue(genericParamName)) {
                        param.put(genericParamName, args[(Integer)entry.getKey()]);
                    }
                }

                return param;
            }
        } else {
            return null;
        }
    }

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值