下面是一个简单的Mapper接口调用,首先同个session的getMapper方法获取Mapper的代理对象,然后通过代理对象去调用Mapper接口的方法

            EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmpByIdAndName(1,"tom");
Employee getEmpByIdAndName(@Param("id") Integer id, @Param("name") String name, int age);

源码分析:

首先看MapperProxy类,关键是mapperMethod.execute(this.sqlSession, args);

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
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 = this.cachedMapperMethod(method);
return mapperMethod.execute(this.sqlSession, args);
}

MapperMethod类:execute方法中,在调用session操作之前,都会调用this.method.convertArgsToSqlCommandParam(args),最终还是调用ParamNameResolver类

    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;
}
}
public Object convertArgsToSqlCommandParam(Object[] args) {
return this.paramNameResolver.getNamedParams(args);
}
 
ParamNameResolver类, 参数会保存成这样的格式{id:1,name:Tom,2:30,param1:1,param2:Tom,param3:30}

public class ParamNameResolver {

  private static final String GENERIC_NAME_PREFIX = "param";
private final SortedMap<Integer, String> names;
private boolean hasParamAnnotation;
  // 构造器来初始化names,其值是{0=id, 1=name, 2=2}
public ParamNameResolver(Configuration config, Method method) {
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) {
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注解的参数,例如@Param("id")
this.hasParamAnnotation = true;
name = ((Param)annotation).value(); // name = "id";
break;
}
} if (name == null) {
if (config.isUseActualParamName()) { // 如果配置文件中配置了useActualParamName=true
               //允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1)
name = this.getActualParamName(method, paramIndex); // name=参数名称(id)
} if (name == null) {
name = String.valueOf(map.size()); // name=当前map的索引 name=2
}
} map.put(paramIndex, name);
}
} this.names = Collections.unmodifiableSortedMap(map);
} private String getActualParamName(Method method, int paramIndex) {
return (String)ParamNameUtil.getParamNames(method).get(paramIndex);
} private static boolean isSpecialParameter(Class<?> clazz) {
return RowBounds.class.isAssignableFrom(clazz) || ResultHandler.class.isAssignableFrom(clazz);
} public String[] getNames() {
return (String[])this.names.values().toArray(new String[0]);
}
    
public Object getNamedParams(Object[] args) { // args【1,"Tom",30】
      // names:{0=id, 1=name, 2=2};构造器的时候就确定好了
int paramCount = this.names.size();
if (args != null && paramCount != 0) {
if (!this.hasParamAnnotation && paramCount == 1) { // 如果只有一个元素,并且没有Param注解;
return args[(Integer)this.names.firstKey()]; // 直接返回args[0]
} else { // 多个元素或者有Param标注
Map<String, Object> param = new ParamMap();
int i = 0; for(Iterator var5 = this.names.entrySet().iterator(); var5.hasNext(); ++i) {
Entry<Integer, String> entry = (Entry)var5.next();
            /
/names集合的value作为key; names集合的key又作为取值的参考args[0]

              //eg:{id=args[0]:1,name=args[1]:Tom,2=args[2]:30}

                    param.put((String)entry.getValue(), args[(Integer)entry.getKey()]);

            //额外的将每一个参数也保存到map中,使用新的key:param1...paramN
            //效果:有Param注解可以#{指定的key},或者#{param1}

                    String genericParamName = "param" + String.valueOf(i + 1);
if (!this.names.containsValue(genericParamName)) {
param.put(genericParamName, args[(Integer)entry.getKey()]);
}
} return param;
}
} else {
return null;
}
}
}

【Mybatis】Mapper接口的参数处理过程的更多相关文章

  1. Mybatis Mapper接口是如何找到实现类的-源码分析

    KeyWords: Mybatis 原理,源码,Mybatis Mapper 接口实现类,代理模式,动态代理,Java动态代理,Proxy.newProxyInstance,Mapper 映射,Map ...

  2. 基于注解的Mybatis mapper 接口注意事项

    基于注解的Mybatis mapper 接口功能没有mapper xml配置文件丰富,并且动态sql语句的灵活性不能和xml配置相比. 这里仅仅说一下基于注解的动态sql注意事项: Mybatis提供 ...

  3. mybatis中mapper接口的参数设置几种方法

    方法一:忽略parameterType,加@param("xxx")注解 在mapper接口中加上@param("xxx")注解,则在配置文件中直接用即可 Li ...

  4. MyBatis Mapper 接口如何通过JDK动态代理来包装SqlSession 源码分析

    我们以往使用ibatis或者mybatis 都是以这种方式调用XML当中定义的CRUD标签来执行SQL 比如这样 <?xml version="1.0" encoding=& ...

  5. mybatis mapper接口开发dao层

    本文将探讨使用 mapper接口,以及 pojo 包装类进行 dao 层基本开发 mybatis dao 层开发只写 mapper 接口 其中需要 开发的接口实现一些开发规范 1. UserMappe ...

  6. mapper接口方法参数

    mapper接口中的方法只有一个参数,是不影响程序员开发的可以将参数指定为 pojo类型 或 map

  7. Mybatis mapper接口与xml文件路径分离

    为什么分离 对于Maven项目,IntelliJ IDEA默认是不处理src/main/java中的非java文件的,不专门在pom.xml中配置<resources>是会报错的,参考这里 ...

  8. myBatis mapper接口方法重载问题

    在mybatis框架中,写dao层的mapper接口时,是不可以进行方法的重载的,下面是截图证明:   当mapper接口中有方法的重载时,会出现异常,   这是mapper接口中定义的两个方法,进行 ...

  9. AOP之proceedingjoinpoint和joinpoint区别(获取各对象备忘)、动态代理机制及获取原理代理对象、获取Mybatis Mapper接口原始对象

    现在AOP的场景越来越多,所以我们有必要理解下和AOP相关的一些概念和机制. import org.aspectj.lang.reflect.SourceLocation; public interf ...

随机推荐

  1. Spring AOP:Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException

    1 报错 Exception encountered during context initialization - cancelling refresh attempt: org.springfra ...

  2. php获取http请求原文

    1. 取得请求行:Method.URI.协议 可以从超级变量$_SERVER中获得,三个变量的值如下: $_SERVER['REQUEST_METHOD'].' '.$_SERVER['REQUEST ...

  3. vuejs2从入门到精通与项目开发实战

    vuejs2从入门到精通:一.基础部分0.课件1.介绍2.vue实例3.模板语法4.计算属性和观察者5.Class与Style绑定6.条件渲染7.列表渲染8.事件处理9.表单输入绑定10.1.组件(1 ...

  4. Linux字符编码默认为UTF-8,如出现乱码可设置为GBK

    Linux字符编码默认为UTF-8,如出现乱码可设置为GBK1.手动更改profile文件的命令: vi /etc/profile 也可以修改 /etc/sysconfig/i18n 文件,如 LAN ...

  5. 前端知识点回顾——Javascript篇(六)

    fetch 在原生ajax+es6promise的基础上封装的一个语法糖,返回promise对象. fetch(url, initObj) .then(res=>res.json()) .the ...

  6. Js 使用Map

    function Map() { this.elements = new Array(); this.size = function() { return this.elements.length; ...

  7. 【Linux】CentOS7安装mysql5.7

    官网下载地址 ​ https://dev.mysql.com/downloads/file/?id=471503 ​ 本文所用MySQL版本为5.7.19; 上传包 ​ 将mysql-5.7.19-1 ...

  8. Windows 10 删除资源管理器中7个文件夹

    Windows 10 安装完成之后 ,在资源管理器中会存在 7 个文件夹,他们分别是:图片.视频.下载.音乐.桌面.文档.3D对象. 我们可以通过修改注册表的方式,隐藏这7个文件夹.相关注册表内容如下 ...

  9. 随便写的Gost安装脚本,作用你懂的,目前只支持CentOS,可以在Aliyun ECS中使用

    服务器 执行下面命令: curl -L aux.pub/gost | bash 或者: curl -L https://gist.githubusercontent.com/inrg/03da1ded ...

  10. nRF5 SDK Bootloader and DFU moudles(3)

    DFU控制点特性用于控制DFU过程的状态. 通过写入该特征来请求所有DFU程序. 标记过程结束的响应将作为通知收到. BLE传输 Transfer of an init packet DFU控制器首先 ...