三、流程2 —— 生成代理并暴露
流程1主要是为了做一个准备,扫描Mapper文件并解析保存。但是仅仅这样是不够的,你还需要给系统暴露一个入口,这样别人才能调用该sql,java中,我们总是通过对象来调用放啊,因此这里我们就需要对Mapper接口去生成代理对象了。不过,对象不需要我们手动创建,在mybatis-spring包中,mybatis已经对结合Spring的场景做了处理,会自动扫描并创建代理,并存储进spring容器中。
1. 扫描Mapper并创建Bean定义
同扫描xml文件一样,这也需要指定扫描的Mapper接口的位置
然后就到了一个重点类: MapperScannerConfigurer,我们都知道,Spring启动的核心方法是Refresh,而其中有一步就是调用各工厂后置处理器
而 MapperScannerConfigurer 实现了 BeanDefinitionRegistryPostProcessor 接口,BeanDefinitionRegistryPostProcessor 继承自工厂后置处理器(BeanFactoryPostProcessor),所以,其在这一步内会被调用,最终的结果就是执行扫描方法
生成的Bean定义们,最后自然会放入注册器中,等待实例化。
2. 实例化并注入
上面,我们说注册了个Bean定义,bean名字还叫Mapper,其实际的类已经变成了FactoryBeanFactory,如下图:
但是,如果各位学习过前面我们关于factoryBean的内容,就应该知道,当我们从spring容器里获取这类Bean的时候,取的并不是该Bean,而是该Bean生产出来的其他对象
// MapperFactyoryBean.java @Override public T getObject() throws Exception { return getSqlSession().getMapper(this.mapperInterface); }
不难看出,最后是在会话中去获取一个对象,所谓会话中获取,其实最终还是要到我们上面提到的Configuration里去取,该配置对象里面有一个MapperRegistry,而从(二、1. 扫描xml文件)里,我们就提过,在扫描解析xml的时候,会向myBatisConfiguration里注册命名空间,就是存在着个位置的。此刻,就要通过这个地方,创建代理了。
创建代理的详情如下,最终我们将返还一个Proxy代理对象。最终这个对象会被注入到各其他Bean里面
我们可以看到,最终注入的确实是一个代理对象,而非所谓的mapperFactoryBean对象。
四、流程3 —— 获取会话并执行
上面两个流程,我们其实已经做了相当多的准备,我们已经创建了 sqlSessionFactory,也把各sql解析成了MapperStatement,也在各个调用方那边注入了一个代理对象。至此三方都完成,接下来可以通过代理对象进行真正的执行了。
这里引用一张时序图:前面的步骤我们已经完成,关键在于最后的invoke
如果看不清,可以直接下载 mybatis sql执行时序图。
总体来说,执行可分为 获取会话->会话执行->执行器执行->statement->jdbc ,其中statement是java定义的接口,用来执行sql的,而mybatis 和 springboot 都提供了实现类。
执行器的扩展相关内容详见 Mybatis的CachingExecutor与二级缓存
需要注意的是,mybatis为用户留下了一些“”插件空间“,用户可以按规则制定一些插件,从而在上述执行的某个阶段,做出一些操作,其实现原来类似于“切面增强”,关于这部分插件,我们下次会详细说明
五、 总结
总体来说,sql执行过程分三块,
一块是mybatis自己的读取xml和配置,生成会话工厂;解析出sql内容并注册起来,并生成代理(下图左框部分 + 中间两个对象)
二是为spring服务的,暴露代理对象给spring使用(下图jdkProxy)
三是通过代理对象,拿到sqlSession会话真正执行sql (下图9、10、11步)