Mybatis源码解析-接口方法参数绑定(旧)

准备工作

public interface UserDao {
    User getUserOne(Integer id, User user, @Param("nameParam") String name, List<String> idList);
}
UserDao userDao = sqlSession.getMapper(UserDao.class);
User user = userDao.getUserOne(1,new User(1,"zhangsan"),"李四");

代理对象
在这里插入图片描述

源码

1)实现InvocationHandler,动态代理

public class MapperProxy<T> implements InvocationHandler, Serializable {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
        	//objet中toStrong、hashCode类似方法,直接反射即可
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            }
			//接口中的默认方法,这里暂时不研究
            if (method.isDefault()) {
                return this.invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable var5) {
            throw ExceptionUtil.unwrapThrowable(var5);
        }
		//获取MapperMethod
        MapperMethod mapperMethod = this.cachedMapperMethod(method);
        //执行sql语句
        return mapperMethod.execute(this.sqlSession, args);
    }
}

1、MapperMethod类内部类SqlCommand command。封装方法全限定名,以及sql标签枚举类型 insert,update,delete,select
在这里插入图片描述
2、MapperMethod类内部类MethodSignature method。封装方法的返回值和参数信息
在这里插入图片描述

2)mapperMethod.execute(this.sqlSession, args) 执行sql语句

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    Object param;
    //根据枚举类型进入增删改查
    switch(this.command.getType()) {
    case INSERT:
        param = this.method.convertArgsToSqlCommandParam(args);
        result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
        break;
    case UPDATE:
        param = this.method.convertArgsToSqlCommandParam(args);
        result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
        break;
    case DELETE:
        param = this.method.convertArgsToSqlCommandParam(args);
        result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
        break;
    case SELECT:
    	//根据返回值的不同进入不同的方法
        if (this.method.returnsVoid() && this.method.hasResultHandler()) {
            this.executeWithResultHandler(sqlSession, args);
            result = null;
        } else if (this.method.returnsMany()) {
            result = this.executeForMany(sqlSession, args);
        } else if (this.method.returnsMap()) {
            result = this.executeForMap(sqlSession, args);
        } else if (this.method.returnsCursor()) {
            result = this.executeForCursor(sqlSession, args);
        } else {
            param = this.method.convertArgsToSqlCommandParam(args);
            result = sqlSession.selectOne(this.command.getName(), param);
            if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {
                result = Optional.ofNullable(result);
            }
        }
        break;
    case FLUSH:
        result = sqlSession.flushStatements();
        break;
    default:
        throw new BindingException("Unknown execution method for: " + this.command.getName());
    }

    if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
        throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
    } else {
        return result;
    }
}

3)param = this.method.convertArgsToSqlCommandParam(args) 获取参数

//内部类
public static class MethodSignature {
    private final ParamNameResolver paramNameResolver;
    ...
    public Object convertArgsToSqlCommandParam(Object[] args) {
    	//方法调用前ParamNameResolver创建对象,看构造函数
        return this.paramNameResolver.getNamedParams(args);
    }
    ...
}   

获取name值,相当于key,后续会用到

public class ParamNameResolver {
    private static final String GENERIC_NAME_PREFIX = "param";
    private final SortedMap<Integer, String> names;
    private boolean hasParamAnnotation;

    public ParamNameResolver(Configuration config, Method method) {
    	//获取参数所有的Class类型
        Class<?>[] paramTypes = method.getParameterTypes();
        //获取每个参数的注解-每个参数可能有多个注解,故二维数组
        Annotation[][] paramAnnotations = method.getParameterAnnotations();
        SortedMap<Integer, String> map = new TreeMap();
        int paramCount = paramAnnotations.length;

        for(int paramIndex = 0; paramIndex < paramCount; ++paramIndex) {
        	// 检测当前的参数类型是否为 RowBounds 或 ResultHandler(是否特殊参数)
            if (!isSpecialParameter(paramTypes[paramIndex])) {
                String name = null;
                Annotation[] var9 = paramAnnotations[paramIndex];
                int var10 = var9.length;
				//多个注解,只获取@param注解中的value值作为name
                for(int var11 = 0; var11 < var10; ++var11) {
                    Annotation annotation = var9[var11];
                    if (annotation instanceof Param) {
                        this.hasParamAnnotation = true;
                        name = ((Param)annotation).value();
                        break;
                    }
                }

                if (name == null) {
                	//useActualParamName 配置项开启,name则为方法上参数名字
                    if (config.isUseActualParamName()) {
                        name = this.getActualParamName(method, paramIndex);
                    }
					//如果没有注解,上面配置项没开启,name则为从0开始的数字字符串
                    if (name == null) {
                        name = String.valueOf(map.size());
                    }
                }

                map.put(paramIndex, name);
            }
        }

        this.names = Collections.unmodifiableSortedMap(map);
    }
}

mybatis配置选项useActualParamName 允许使用方法签名中的名称作为参数名称,但必须采用java8编译,并且加上-parameters选项
不满足以上条件,则会变成arg0、arg1…
在这里插入图片描述
private final SortedMap<Integer, String> names; 两种方式结果
在这里插入图片描述
在这里插入图片描述

4)核心代码

public Object getNamedParams(Object[] args) {
    int paramCount = this.names.size();
    if (args != null && paramCount != 0) {
    	//如果没@Param注解,且参数只有一个
        if (!this.hasParamAnnotation && paramCount == 1) {
            return args[(Integer)this.names.firstKey()];
        } else {
            Map<String, Object> param = new ParamMap();
            int i = 0;
			//遍历上面的map,将names value及arg0或者id作为key,通过names key作为索引获取arg[key]参数
            for(Iterator var5 = this.names.entrySet().iterator(); var5.hasNext(); ++i) {
                Entry<Integer, String> entry = (Entry)var5.next();
                param.put((String)entry.getValue(), args[(Integer)entry.getKey()]);
                //再添加一组 key = param1  value  = 参数组的数组
                String genericParamName = "param" + String.valueOf(i + 1);
                if (!this.names.containsValue(genericParamName)) {
                    param.put(genericParamName, args[(Integer)entry.getKey()]);
                }
            }

            return param;
        }
    } else {
        return null;
    }
}
  • 如果参数没有@Param注解,且参数只有一个,直接通过Object[] args的arg[0]获取参数,此时参数名称无论写什么都可以

在这里插入图片描述

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冬天vs不冷

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值