前言
在学习之前需要复习以下知识:
- 动态代理
- JDBC操作
- 了解Spring容器的创建流程
一、向Spring容器中注入Mapper接口代理对象
1.注册MapperScannerConfigurer组件的 BeanDefinition ,并在void registerBeanDefinitions 方法中为MapperScannerConfigurer组件设置@MapperSacn注解中所配置的属性信息;
MapperScannerRegistrar.java
2.MapperScannerConfigurer组件实现了BeanDefinitionRegistryPostProcessor (spring扩展接口),public void postProcessBeanDefinitionRegistry()方法 - 用于加载用户的自定义配置(一般可以在配置文件中配置多数据源的sqlSessionFactory)以及它会查找类路径下的映射器并自动将它们创建成 MapperFactoryBean(MapperFactoryBean 会将Mapper接口代理对象注入到Spring容器中)。
MapperScannerConfigurer.java
在上面的后置处理器方法中创建了ClassPathMapperScanner对象,并调用了scanner.scan()方法对指定映射包下的Mapper接口进行扫描。
3.ClassPathMapperScanner重写了父类的doScan()方法,调用父类ClassPathBeanDefinitionScanner的doScan()方法获取到指定包下的所有 Mapper接口的BeanDefinition并返回,并且注册了Mapper接口的BeanDefinition。
ClassPathBeanDefinitionScanner.java
4.ClassPathMapperScanner在processBeanDefinitions()方法中修改了这些 Mapper接口的 beanclass 为MapperFactoryBean。


5.由于SpringBoot在Mybatis自动配置文件中已经配置了SqlSessionTemplate、SqlSessionFactory的bean,所以我们无需配置。


6.Spring会自动装配MapperFactoryBean所需要的对象,在注入了 SqlSessionTemplate、SqlSessionFactorybean之后,会调用setSqlSessionFactory()方法为每个 MapperFactoryBean对象注入sqlSessionTemplate
SqlSessionDaoSupport.java
7.在MapperFactoryBean的属性设置之后,就会调用checkDaoConfig()方法。
MapperFactoryBean.java
8.接下来正式到了mybatis的核心,通过MapperFactoryBean的getObject()方法向Spring容器注入不同Mapper接口代理对象。
MapperFactoryBean.java
9.调用SqlSessionTemplate的getMapper(Class<T> type)方法,通过封装在SqlSessionTemplate中的配置类Configuration获取代理对象。
SqlSessionTemplate.java
10.最终通过不断的调用,使用mybatisMapperRegistry来获取 Mapper接口的代理对象。

11.在knownMappers中保存了每个类型的Mapper对应的MapperProxyFactory代理工厂,调用mapperProxyFactory.newInstance(sqlSession)创建代理对象。
MybatisMapperRegistry.java
12.最终会创建一个代理对象,mapperProxy中实现了动态代理的调用处理方法。
MybatisMapperProxyFactory.java
至此一个Mapper接口的代理对象就注入到了Spring容器当中。
二、代理对象的执行流程
接下来分析在MybatisMapperProxy的invoke()方法做了什么。
1.当调用Mapper接口代理对象时,代理对象就会执行invoke()方法。
MybatisMapperProxy.java
2.调用mapperMethod.execute(sqlSession, args)方法,根据SQL类型,使用sqlSession调用不同的方法(这里的sqlSession就是Spring整合mybatis创建的SqlSessionTemplate)。

3.分析SELECT分支,调用链进入到SqlSessionTemplate其中的一个selectOne方法,可以发现在SqlSessionTemplate中使用了sqlSessionProxy代理对象调用的方法(后面会分析为何要使用代理对象调用)。

4.接下来会先执行sqlSessionProxy代理对象的invoke()方法,获取DefaultSqlSession对象,并使用DefaultSqlSession对象来执行SQL操作。

5.沿着调用链来到了DefaultSqlSession的selectList()方法,使用configuration获取到了MappedStatement对象,这里面封装了需要执行的SQL语句等信息,通过执行器将MappedStatement以及SQL参数parameter等传入方法。

6.经过漫长的调用链最终会进入到MybatisSimpleExecutor的doQuery方法,可以看到熟悉的JDBC操作,并且会在prepareStatement()方法中获取数据库连接,并返回一个Statement,最终调用PreparedStatement对象执行SQL语句。



三、总结
前面两部分可以大致分为 Mapper接口代理对象的创建,以及代理对象的对SQL语句的执行。
- Mapper接口代理对象的创建:Spring通过
@MapperScan注解获取到所有的Mapper接口,通过MapperScannerRegistrar向容器中注册了一个MapperScannerConfigurer组件并为其设置了注解属性信息,在这个组件中创建了ClassPathMapperScanner对象扫描指定包下的Mapper接口组件还将设置的属性信息也设置到了ClassPathMapperScanner对象中,ClassPathMapperScanner对象将扫描到的Mapper接口的beanDefinition的beanclass修改为MapperFactoryBean,MapperFactoryBean通过getObject()方法调用SqlSessionTemplate.getMapper(this.mapperInterface)方法返回Mapper接口的动态代理对象; - 代理对象对SQL语句的执行:代理对象在
invoke()方法中获取到调用方法类型并根据SQL类型执行DefaultSqlSession不同的SQL方法,在DefaultSqlSession中封装了Configuration对象,它会获取到MappedStatement对象(包含了需要执行的SQL语句),后面会获取数据库连接使用PreparedStatement对象执行SQL语句。
四、补充
1.前面我们提到在SqlSessionTemplate调用SQL方法时,真正执行的是封装在SqlSessionTemplate里的sqlSessionProxy动态代理对象,使用代理对象执行SQL方法的目的是保证线程安全(DefaultSQLSession不是线程安全的),具体参考:SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的?
版权声明:本文为qq_43327091原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。