第五章-Mybatis源码解析-执行流程(三)

5.2 Mapper对象间接执行

先看示例代码

BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
blog = mapper.selectBlog1(1);

第一章的 准备示例中了解到,BlogMapper 是一个接口,并没有具体实现类,当调用 mapper.selectBlog1(1) 时,到底是谁在起作用,这里先说下答案,里面用到的是Java的两大核心法宝:反射和代理。我们沿着这个思路继续前进。

5.2.1 通过代理和反射创建Mapper接口的实现

sqlSession.getMapper 的调用链如下:

DefaultSqlSession.getMapper -> configuration.getMapper -> mapperRegistry.getMapper,最终进到 mapperRegistry.getMapper,看代码:

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    // 首先获取 mapper的代理工厂对象,这个对象是mybatis启动时设置的,设置过程见章节`5.2.2`
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
        throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
        // 代理模块创建类型为 type 的实现,进到 MapperProxyFactory.newInstance 方法
        return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
        throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
}

**MapperProxyFactory.newInstance **

public T newInstance(SqlSession sqlSession) {
    // 创建代理对象 MapperProxy 就是 InvocationHandler 子类
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
}

protected T newInstance(MapperProxy<T> mapperProxy) {
    // 这一步就是Java自带的代理方式调用,关于代理不会的,可以随便网上搜索下代理的使用。这一步执行完后,就算是创建了接口的代理实现类了,后续调用该接口的方法时,就会进到 InvocationHandler 这里就是 MapperProxy 的 invoke方法中,继续往里走
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface 		}, mapperProxy);
}

MapperProxy 的 invoke 方法

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
        // 判断是否 Object 类
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        } else if (method.isDefault()) { // 判断是否默认方法,jdk1.8以后的功能,在接口里的方法上,增加default修饰
            return invokeDefaultMethod(proxy, method, args);
        }
    } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
    }
    // 直接看这一步
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    // 执行方法
    return mapperMethod.execute(sqlSession, args);
}

private MapperMethod cachedMapperMethod(Method method) {
    // 如果 methodCache 中没有,则放入一个新的 MapperMethod 对象,有则不放。接着看看 MapperMethod 是什么,以及它做了什么
    return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}

new MapperMethod

public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    // 这一步的创建,是为了后续执行时确定接口对应的是select|insert|update|delete
    this.command = new SqlCommand(config, mapperInterface, method);
    this.method = new MethodSignature(config, mapperInterface, method);
}

public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
    final String methodName = method.getName(); // 方法名
    final Class<?> declaringClass = method.getDeclaringClass(); // 该方法所在的类型
    // 从 configuration 中拿到已解析的 MappedStatement
    MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
                                                configuration);
    if (ms == null) {
        // 没有已解析的语句
        if (method.getAnnotation(Flush.class) != null) {
            // 如果注解中有Flush
            name = null;
            type = SqlCommandType.FLUSH;
        } else {
            // 没有,抛出异常,无效语句
            throw new BindingException("Invalid bound statement (not found): "
                                       + mapperInterface.getName() + "." + methodName);
        }
    } else {
        // 从已解析的语句中,拿到id和type(select|insert|update|delete)
        name = ms.getId();
        type = ms.getSqlCommandType();
        if (type == SqlCommandType.UNKNOWN) {
            // 未知类型,抛异常
            throw new BindingException("Unknown execution method for: " + name);
        }
    }
}

private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
                                               Class<?> declaringClass, Configuration configuration) {
    // 类型全限定名+方法名,代表语句id,与XXXMapper.xml中编写的sql语句id,一一对应
    String statementId = mapperInterface.getName() + "." + methodName;
    // 已经解析过这个语句,直接取出来就行
    if (configuration.hasStatement(statementId)) {
        return configuration.getMappedStatement(statementId);
    } else if (mapperInterface.equals(declaringClass)) {
        // 没有解析过该语句,并且本身没有再继承其他接口,直接返回 null
        return null;
    }
    // 如果还有继承其他接口,则遍历它们
    for (Class<?> superInterface : mapperInterface.getInterfaces()) {
        if (declaringClass.isAssignableFrom(superInterface)) {
            // 递归
            MappedStatement ms = resolveMappedStatement(superInterface, methodName,
                                                        declaringClass, configuration);
            if (ms != null) {
                return ms;
            }
        }
    }
    return null;
}
}

执行方法

// 最终执行走到这个方法,因为前面对insert|select已经做了细致的铺垫,所以,这一段代码就非常容易理解了
public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
        case INSERT: {
            Object param = method.convertArgsToSqlCommandParam(args);
            // insert 直接看章节`5.1.2`
            result = rowCountResult(sqlSession.insert(command.getName(), param));
            break;
        }
        case UPDATE: {
            // 参考 insert|select 读者自行阅读源码扩展
            Object param = method.convertArgsToSqlCommandParam(args);
            result = rowCountResult(sqlSession.update(command.getName(), param));
            break;
        }
        case DELETE: {
            // 参考 insert|select 读者自行阅读源码扩展
            Object param = method.convertArgsToSqlCommandParam(args);
            result = rowCountResult(sqlSession.delete(command.getName(), param));
            break;
        }
        case SELECT:
            if (method.returnsVoid() && method.hasResultHandler()) {
                // 自带 ResultHandler 的方式,将在`第六章`和`第七章`中讲
                executeWithResultHandler(sqlSession, args);
                result = null;
            } else if (method.returnsMany()) {
                // 涉及多个返回值的,一般指存储过程的执行
                result = executeForMany(sqlSession, args);
            } else if (method.returnsMap()) {
                // 当对应的mapper接口方法,通过 @MapKey 注解标注,并且返回值是Map的时候,一般用在多表连接查询,又不想创建新的 domain 对象时,可以这样组装成 Map
                result = executeForMap(sqlSession, args);
            } else if (method.returnsCursor()) {
                // 返回类型为 Cursor
                result = executeForCursor(sqlSession, args);
            } else { // 直接走到这里
                // 参数转换
                Object param = method.convertArgsToSqlCommandParam(args);
                // 这里又到了真正执行的地方,看章节`5.1.1`
                result = sqlSession.selectOne(command.getName(), param);
                if (method.returnsOptional()
                    && (result == null || !method.getReturnType().equals(result.getClass()))) {
                    result = Optional.ofNullable(result);
                }
            }
            break;
        case FLUSH:
            result = sqlSession.flushStatements();
            break;
        default:
            throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
        throw new BindingException("Mapper method '" + command.getName()
                                   + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
}

5.2.2 Mapper代理工厂的设置

调用链:new SqlSessionFactoryBuilder().build(inputStream) -> XMLConfigBuilder.parse() -> XMLConfigBuilder.mapperElement -> configuration.addMapper(mapperInterface) -> mapperRegistry.addMapper

public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) { // 必须是接口类型
        if (hasMapper(type)) { // 判断重复
            throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
        }
        boolean loadCompleted = false;
        try {
            // knownMappers 是一个HashMapp,往里放入 MapperProxyFactory 对象,由于这一步操作是从build()进来的,mybatis只会build一次,所以 MapperProxyFactory 对象也只会有这一个,后续所有的 mapper 接口的实现类都由它来代理实现
            knownMappers.put(type, new MapperProxyFactory<>(type));
            // 后面这几行代码与addMapper的主流逻辑已经不相关了,这里主要是针对Mybatis的注解形式,后续章节会单独讲注解形式的源码
            MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
            parser.parse();
            loadCompleted = true;
        } finally {
            if (!loadCompleted) {
                knownMappers.remove(type);
            }
        }
    }
}
  • 12
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

多栖码农

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

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

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

打赏作者

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

抵扣说明:

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

余额充值