Mybatis中Mapper接口如何和xml中sql关联(二)

继续上一篇,了解到,MyBatis中利用的JDK的动态代理,实现了接口管联xml中的sql,这里呢,只是知道了用什么方式,但是具体的怎么关联不清楚,需要再次学习其中的内容,go。

知道了动态代理后,先看MapperProxy类,他实现了InvocationHandler,那么也重写了invoke方法,找个这个方法
MapperProxy.class 中

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        } else {
            MapperMethod mapperMethod = this.cachedMapperMethod(method);
            return mapperMethod.execute(this.sqlSession, args);
        }
    }

都知道执行ActivityCzMapper 接口的方法,会调用这里MapperProxy的invoke方法,看到第一行代码if判断

Object.class.equals(method.getDeclaringClass())

这里的method肯定是我们调用ActivityCzMapper 接口的方法,getDeclaringClass()肯定返回null, 因为我们的Mapper接口就没有实现类,不可能走下去,所以执行else里的代码,else里面执行了cachedMapperMethod()方法,我们看一下

private MapperMethod cachedMapperMethod(Method method) {
        MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);
        if(mapperMethod == null) {
            mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
            this.methodCache.put(method, mapperMethod);
        }

        return mapperMethod;
    }

首先在methodCache的map中get传来的method(就是访问Mapper接口的方法)key,结果肯定是null,执行下面代码初始化MapperMethod三个参数的构造方法,第一个参数Mapper接口,第二个接口的方法,第三个是一个sqlSession的getConfiguration方法代表sqlsession的所有信息,在上一篇中传递的那个sqlsession,初始化MapperProxy类的构造方法时。

现在跳到这个MapperMethod的构造方法查看
MapperMethod.class中

public MapperMethod(Class mapperInterface, Method method, Configuration config) {
        this.command = new MapperMethod.SqlCommand(config, mapperInterface, method);
        this.method = new MapperMethod.MethodSignature(config, method);
    }

看到初始化两个成员变量,command,method,在继续看SqlComman()方法是干什么的,跳到MapperProxyFactory类中的静态内部类SqlCommand,看到代码

public static class SqlCommand {
        private final String name;
        private final SqlCommandType type;

        public SqlCommand(Configuration configuration, Class mapperInterface, Method method) throws BindingException {
            String statementName = mapperInterface.getName() + "." + method.getName();
            MappedStatement ms = null;
            if(configuration.hasStatement(statementName)) {
                ms = configuration.getMappedStatement(statementName);
            } else if(!mapperInterface.equals(method.getDeclaringClass().getName())) {
                String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName();
                if(configuration.hasStatement(parentStatementName)) {
                    ms = configuration.getMappedStatement(parentStatementName);
                }
            }

            if(ms == null) {
                throw new BindingException("Invalid bound statement (not found): " + statementName);
            } else {
                this.name = ms.getId();
                this.type = ms.getSqlCommandType();
                if(this.type == SqlCommandType.UNKNOWN) {
                    throw new BindingException("Unknown execution method for: " + this.name);
                }
            }
        }

        public String getName() {
            return this.name;
        }

        public SqlCommandType getType() {
            return this.type;
        }
    }

哇,看这一条代码

String statementName = mapperInterface.getName() + "." + method.getName();

这个 mapperInterface.getName()就是Mapper接口的类名字,他必然和xml的
一致,method.getName()得到方法的名字,
那就是mapper里的id,然后判断

MappedStatement ms = null;
if(configuration.hasStatement(statementName)) {
                ms = configuration.getMappedStatement(statementName);
            }

成立后,看下MappedStatement的意思,MappedStatement对象对应Mapper配置文件中的一个select/update/insert/delete节点,主要描述的是一条SQL语句。
这样把他包装起来,在执行下面的代码,ms != null 后赋值,name 就是mapper xml 中sql 的id值,type就是sql的类型(sel,up,de)如果不知道,那就异常。

OK现在我们返回到MapperMethod中,说白了,传来的sqlSession到SqlCommand,在到MapperMethod。
现在在回到MapperProxy的invoke方法中走最后一步, return mapperMethod.execute(this.sqlSession, args);
查看execute方法

public Object execute(SqlSession sqlSession, Object[] args) {
        Object param;
        Object result;
        if(SqlCommandType.INSERT == this.command.getType()) {
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
        } else if(SqlCommandType.UPDATE == this.command.getType()) {
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
        } else if(SqlCommandType.DELETE == this.command.getType()) {
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
        } else {
            if(SqlCommandType.SELECT != this.command.getType()) {
                throw new BindingException("Unknown execution method for: " + this.command.getName());
            }

            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 {
                param = this.method.convertArgsToSqlCommandParam(args);
                result = sqlSession.selectOne(this.command.getName(), param);
            }
        }

        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;
        }
    }

有点多,但是很明确,清楚看出来,首先判断sql的类型,up,sel,de的,在select中还有一层判断Mapper接口方法的返回值,我们查的是selectByPrimaryKey单个的,那就走selectOne()方法,例如我们的返回值List,那需要走executeForMany,可以看下这个源代码:

private  Object executeForMany(SqlSession sqlSession, Object[] args) {
        Object param = this.method.convertArgsToSqlCommandParam(args);
        List result;
        if(this.method.hasRowBounds()) {
            RowBounds rowBounds = this.method.extractRowBounds(args);
            result = sqlSession.selectList(this.command.getName(), param, rowBounds);
        } else {
            result = sqlSession.selectList(this.command.getName(), param);
        }

        return !this.method.getReturnType().isAssignableFrom(result.getClass())?(this.method.getReturnType().isArray()?this.convertToArray(result):this.convertToDeclaredCollection(sqlSession.getConfiguration(), result)):result;
    }

恩,很稳,贴上一句关键点

 result = sqlSession.selectList(this.command.getName(), param, rowBounds);

这就不多说了,将结果返回。这样通过MapperProxy的 public Object invoke(Object proxy, Method method, Object[] args)方法
返回结果。这样就是一个执行Mapper接口方法执行sql的过程。在整个过程中还有很多地方未探索的,就比如在mybatis配置文件加载的时候干了什么,例如那个addMapper()方法到底在MyBatis哪个加载过程中add的,需要再次探索。以上是我的整个学习过程。

你可能感兴趣的:(Mybatis中Mapper接口如何和xml中sql关联(二))