11.0 Spring整合Mybatis原理
一、整合核心思路
由很多框架都需要和Spring进行整合,而整合的核心思想就是把其他框架所产生的对象放到Spring容器中,让其成为Bean,只要成为了Bean,在Spring项目中就能很方便的使用这些对象了,也就能很方便的使用Mybatis框架所提供的功能了。
二、Mybatis-Spring 1.3.2源码执行流程
- 通过
@MapperScan
导入了MapperScannerRegistrar类 - MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,所以Spring在启动 时会调用MapperScannerRegistrar类中的registerBeanDefinitions方法
- 在registerBeanDefinitions方法中定义了一个ClassPathMapperScanner对象,用来扫描 mapper
- 设置ClassPathMapperScanner对象可以扫描到接口,因为在Spring中是不会扫描接口的
- 同时因为ClassPathMapperScanner中重写了isCandidateComponent方法,导致 isCandidateComponent只会认为接口是备选者Component
- 通过利用Spring的扫描后,会把接口扫描出来并且得到对应的BeanDefinition
- 接下来把扫描得到的BeanDefinition进行修改,把BeanClass修改为MapperFactoryBean,把 AutowireMode修改为byType
- 扫描完成后,Spring就会基于BeanDefinition去创建Bean了,相当于每个Mapper对应一个 FactoryBean
- 在MapperFactoryBean中的getObject方法中,调用了getSqlSession()去得到一个sqlSession 对象,然后根据对应的Mapper接口生成一个Mapper接口代理对象,这个代理对象就成为 Spring容器中的Bean
- sqlSession对象是Mybatis中的,一个sqlSession对象需要SqlSessionFactory来产生
- MapperFactoryBean的AutowireMode为byType,所以Spring会自动调用set方法,有两个set 方法,一个setSqlSessionFactory,一个setSqlSessionTemplate,而这两个方法执行的前提是 根据方法参数类型能找到对应的bean,所以Spring容器中要存在SqlSessionFactory类型的 bean或者SqlSessionTemplate类型的bean。
- 如果你定义的是一个SqlSessionFactory类型的bean,那么最终也会被包装为一个 SqlSessionTemplate对象,并且赋值给sqlSession属性
- 而在SqlSessionTemplate类中就存在一个getMapper方法,这个方法中就产生一个Mapper接 口代理对象
- 到时候,当执行该代理对象的某个方法时,就会进入到Mybatis框架的底层执行流程
Spring整合Mybatis之后SQL执行流程
三、Mybatis-Spring 2.0.6源码执行流程
- 通过
@MapperScan
导入了MapperScannerRegistrar类 - MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,所以Spring在启动 时会调用MapperScannerRegistrar类中的registerBeanDefinitions方法
- 在registerBeanDefinitions方法中注册一个MapperScannerConfigurer类型的 BeanDefinition
- 而MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,所以 Spring在启动过程中时会调用它的postProcessBeanDefinitionRegistry()方法
- 在postProcessBeanDefinitionRegistry方法中会生成一个ClassPathMapperScanner对象,然 后进行扫描
- 后续的逻辑和1.3.2版本一样。 带来的好处是,可以不使用@MapperScan注解,而可以直接定义一个Bean,比如:
@Bean
public MapperScannerConfigurer mapperScannerConfigurer() {
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setBasePackage("com.luban");
return mapperScannerConfigurer;
}
四、Spring整合Mybatis后一级缓存失效问题
从Spring整合Mybatis之后SQL执行流程可知,Mybatis中的一级缓存是基于SqlSession来实现的,所以在执行同一个sql时,如果使用的是同一个 SqlSession对象,那么就能利用到一级缓存。 但是在Spring整合Mybatis后,如果执行某个方法时,该方法上没有加@Transactional注解,也就是没有开启Spring事务,每执行一个sql时都会新生成一个SqlSession 对象来执行该sql,这就是我们说的一级缓存失效(也就是没有使用同一个SqlSession对象)
其实这个不算问题,只是存在的生命周期太短了(执行完一个sql后就被销毁了,下一个sql执行时又 是一个新的SqlSession了)。