【MyBatis】执行原理(三):获取代理对象(MapperProxy) 源码分析

Mybatis 专栏收录该内容
22 篇文章 0 订阅

通过前两篇的分析,我们已经了解了 SqlSessionFactory,SqlSession 底层的逻辑。

String resource = "mybatis-config.xml"; 
InputStream inputStream = Resources. getResourceAsStream(resource); 
// build 最终返回了 SqlSessionFactory 接口的默认实现 DefaultSqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

// openSession 最终返回SqlSession接口的一个默认实现DefaultSqlSession 
SqlSession session = sqlSessionFactory.openSession(); 

现在我们已经有一个 DefaultSqlSession 了,下一步工作就是找到 Mapper.xml 里面定义的 Statement ID,才能执行对应的SQL语句。

找到StatementID有两种方式:

  1. 硬编码:直接调用session的方法,在参数里面传入Statement ID

    Blog blog = (Blog) session.selectOne("com.my.mapper.BlogMapper.selectBlogById ", 1);
    

    这种方式属于硬编码,我们没办法知道有多少处调用,修改起来也很麻烦。另一个问题是如果参数传入错误,在编译阶段也是不会报错的,不利于预先发现问题。所以在实际开发中不推荐使用。

  2. 约定:在 MyBatis 后期的版本提供了第二种方式,就是定义一个接口,然后再调用Mapper接口的方法。

    由于我们的接口名称跟 Mapper.xml 的 namespace 是对应的,接口的方法跟statement ID也都是对应的,所以根据方法就能找到对应的要执行的SQL。

    BlogMapper mapper = session.getMapper(BlogMapper.class);
    Blog blog = mapper.selectBlogById(1); 
    

我们在实际工作中般都是采用的第二种方式。由于 BlogMapper 只是一个接口,所以很容易想到 SqlSession#getMapper() 会给我们生成一个具体的代理对象,并且实现了 BlogMapper 接口。

下面,我们就来分析 Mapper 对象相关源码…

getMapper()

// 1.DefaultSqlSession#getMapper()
// 调用了 Configuration的getMapper()方法。
public <T> T getMapper(Class<T> type) {
  return configuration.getMapper(type, this);
}

// 2.Configuration#getMapper()
// 调用了MapperRegistry 的getMapper()方法。
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
 }

// 3.MapperRegister#getMapper()
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
	// 获取MapperProxy工厂
    // 在解析mapper标签和Mapper.xml的时候已经把接口类型和类型对应的MapperProxyFactory放到了一个Map中。
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      // 从相应的 mapperProxyFactory 工厂获取具体对象
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

newInstance()

// MapperProxyFactory.newInstance()
public T newInstance(SqlSession sqlSession) {
	// MapperProxy 是代理类,它实现了 InvocationHandler 接口
	// 构造代理对象时的入参是:SqlSession,mapper接口,方法缓存
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
  
// 通过JDK动态代理生成代理对象
protected T newInstance(MapperProxy<T> mapperProxy) { 
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] {mapperInterface }, mapperProxy);
  }

到这里我们可以看到已经生成了代理对象。

问题:我们常常使用的JDK动态代理和MyBatis用到的JDK动态代理有什么区别呢?

我们平常使用JDK动态代理时,需要在实现了InvocationHandler的代理类里面传入(组合)一个被代理对象的实现类,让这个被代理对象去执行接口中的方法。
在这里插入图片描述

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
	method.invoke(ProxyImpl, args);
}

而对于 Mybatis 动态代理,它代理的 Mapper 接口并没有实现类,因为所有 Mapper 接口中的方法是都是交给 SqlSession 的 Excutor 去执行,并不需要一个实现类。

因为我们唯一要做的就是根据接口类型+方法的名称找到StatementID。执行 Mapper 接口中方法的是 SqlSession 的执行器 Executor。

MapperProxy 唯一要做的事情就是根据接口类型(namespace)+ 方法名(sqlId)得到 statementID,然后再调用 SqlSession 的 insert()、delete()、update()、selectOne()、selectList() 等方法去执行。下面 MapperProxy 部分源码:


关于invoke的逻辑,及如何执行sql的放在下一篇去说…

=>总结

获得Mapper对象的过程,实质上是获取了一个 MapperProxy 的代理对象。MapperProxy 中有 sqlSession、mapperInterface、methodCache。

在这里插入图片描述

  • 1
    点赞
  • 1
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

<p> 本课程适合有JAVA和数据库基础的人员。 </p> <p> 本课程使用Eclipse和<span style="font-size:13.3333px;">IntelliJ IDEA两种开发工具,详细的讲解了MyBatis的各种语法,并且讲解了MyBatis逆向工程和MyBatis两种常用的插件MyBatis Plus和通用Mapper。</span> </p> <p> <span style="font-size:13.3333px;">本课程从理论和实际案例两方面充分讲解了MyBatis的各种技术细节,和应用场景,并且以绘图的方式讲解了各种MyBatis中较难的技术点。</span> </p> <p> <span style="font-size:13.3333px;">相信可以通过本课程的学习,读者能够掌握MyBatis学习过程中的各种技巧和实际案例。</span> </p> <p> <span style="font-size:13.3333px;">本课程中设计的大致技术点,如下所示。</span> </p> <p>       1.Eclipse、IntelliJ IDEA环境下开发MyBatis </p> <span></span>      2.MyBatis多种方式的CRUD<br />       3.MyBatis配置文件详解<br />       4.MyBatis映射文件详解<br />       5.使用MyBatis调用存储过程<br />       6.动态SQL<br />       7.关联查询<br />       8.延迟加载<br />       9.整合一级、二级缓存<br />       10.逆向工程<br />       11.事务操作<br />       12.MyBatis处理多个参数的问题<br />       13.鉴别器和别名<br />       14.各种方式的模糊查询<br />       15.MyBatis核心源码分析<br />       16.MyBatis拦截器<br />       17.MyBatis批量更新操作<br />       18.PageHelper<br />       19.MyBatis Plus详解<br />       20.通用Mapper详解<br /><p>    希望大家可以通过本课程的学习,深入的掌握MyBatis及其各种插件的用法,从而提高对数据的操作效率 </p> <p> <br /></p>
相关推荐
©️2020 CSDN 皮肤主题: 撸撸猫 设计师:马嘣嘣 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值