框架源码专题-Spring-6、整合Mybatis
1. 环境准备 ConfigurationClassPostProcessor
https://www.processon.com/diagraming/63e4bfa27c423a1934f127a5
❕
1.1. 注册 BPP 的 BD
1.2. 创建 BPP 的 Bean
2. 注册 MapperScannerConfigurer
2.1. 注册 MapperScannerConfigurer BD
2.1.1. @Import(MapperScannerRegistrar)
Spring-1、基本原理@Import 使用方法
❕
可以发现@MapperScan 注解中使用的是@Import 的第 2 种用法,导入一个实现了 ImportBeanDefinitionRegistrar 的类 MapperScannerRegistrar.class
@MapperScan 注解的属性不为空,说明配置了这个注解,就会调用分支里的重写的方法 registerBeanDefinitions
2.1.2. 建造者构造 MapperScannerConfigurer BD
在 MapperScannerRegistrar.class
的方法 registerBeanDefinitions
中用创建 bean 定义构造器 BeanDefinitionBuilder
,并通过构造器构建出 MapperScannerConfigurer
的 bean 定义
应用到了设计模式:建造者设计模式
然后将构建的 MapperScannerConfigurer 的 bean 定义注册到容器中
2.1.3. 注册流程 loadBeanDefinitionsFromRegistrars
ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry
this.reader.loadBeanDefinitions
loadBeanDefinitionsForConfigurationClass
loadBeanDefinitionsFromRegistrars
ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry
走完除了 Mapper 接口对应的 BD,其他的包括 MapperScannerConfigurer
在内都已注册 BD。接下来走 MapperScannerConfigurer
的 postProcessBeanDefinitionRegistry 才开始注册 Mapper 的 BD。❕
2.2. 创建 MapperScannerConfigurer Bean
因为 MapperScannerConfigurer 是 BeanDefinitionRegistryPostProcessor
的实现类
2.3. MapperScannerConfigurer 继承关系⭐️🔴
3. 扫描 Mapper 接口成为 BD
上面一步注册了MapperScannerConfigurer 的 BD 信息,接下来看 MapperScannerConfigurer
的作用。
类 MapperScannerConfigurer 实现了 BeanDefinitionRegistryPostProcessor
接口中的 postProcessBeanDefinitionRegistry
方法,这个方法也可以为容器中注册 bean 定义
3.1. 触发时机⭐️🔴
MapperScannerConfigurer.postProcessBeanDefinitionRegistry
3.2. 扫描原理 (ClassPathMapperScanner)⭐️🔴
https://www.processon.com/diagraming/63f1d3dea600c6676369dd8f
❕
实现 BeanDefinitionRegistryPostProcessor 是IOC 的一个扩展点(Spring-2、IOC)可以自主的在 bean 的概念态到定义态的过程中,往容器里注入 bean 定义
BeanDefinitionRegistryPostProcessor 中的 postProcessBeanDefinitionRegistry()
方法会在 IOC 容器中注册@MapperScan 扫描到的 mapper 包下的 bean 定义
3.2.1. 设置过滤规则
3.2.2. 设置 BD 判断规则
3.2.2.1. Spring 默认的判断规则
isIndependent 判断当前 BeanDefinition 对应的类是否是独立类(顶级类,静态内部类)**顶层的类 (独立类)**(没有父类或静态内部类) ❕
isConcrete 是否非接口非抽象类
hasAnnotatedMethods(Lookup.class.getName()) 是否有包含@Lookup 注解的方法
成立条件:首先必须是独立类,其次要么是非抽象类非接口,要么是抽象类但是有@Lookup 注解的方法
3.2.2.2. Mybatis 重写的判断规则
扫描独立的接口
3.2.3. 扫描过程
4. mapper 实例化 (mapper 是接口原本无法实例化)
4.1. processBeanDefinitions(偷梁换柱)⭐️🔴
ClassPathMapperScanner将接口类型的 mapper 的 definition 类型改为 MapperFactoryBean
4.2. MapperFactoryBean⭐️🔴
❕
4.3. JDK 动态代理接口设置
指定生成代理类时使用的是有参构造器,并且入参是我们的 mapper 接口
4.4. 修改注入模式 -byType⭐️🔴
4.4.1. 原因 1 非空判断
是因为 MapperFactoryBean 继承于 SqlSessionDaoSupport,SqlSessionDaoSupport 里有个属性 sqlSessionTemplate 有非空断言。因为属性上没有@Autowired,默认情况下是无法自动装配的。所以要改成按类型,在 IOC 里查找到之后,调用 set 方法,进行赋值就可以完成自动装配
Spring 中 AbstractBeanDefinition.AUTOWIRE_BY_TYPE
默认设置为 0,需要加@Autowired 才能完成自动装配。如果没有显示加@Autowired,那么就必须修改自动注入模式。
❕
4.4.2. 原因 2 不易报错
sqlSessionTemplate 必须赋值,改为 by_type 赋值不容易出错
5. mapper 使用逻辑
5.1. 生成动态代理并缓存 (SqlSessionFactoryBean)
5.1.1. 流程图
https://www.processon.com/diagraming/63f04e03e39b2d4955a8c9cc
5.1.2. 缓存 mapper 接口信息
5.1.2.1. SqlSessionFactoryBean 继承关系
实现了 InitializingBean
的 afterPropertiesSet()
方法
5.1.2.2. 缓存时机
5.1.3. 生成动态代理⭐️🔴⭐️🔴
- 由
MapperScannerConfigurer
来扫描对应的 Mapper 接口的 BD 信息,并将类型替换为MapperFactoryBean
。 - 由
SqlSessionFactoryBean
来扫描 mapper 接口并配置与MapperProxyFactory
的对应关系,保存到 mapperRegistry 中的knownMappers
Map 中。 - 自动装配 Mapper 时,从容器获取 mapper 但由于实现了 FactoryBean 接口,所以由 getObject 方法生成。在 getObjects 方法中,最终调用到 mapperRegistry 中的 getMapper 方法,调用
MapperProxyFactory
的newInstance
生成对应的MapperProxy
即 Mapper 接口的动态代理。 ❕
5.2. 使用动态代理
当从 Spring 中获取 Mapper 接口时,将会调用对应的 MapperFactoryBean 的 getObjects 方法,该方法返回值即为对应的 MapperProxyFactory 创建的 MapperProxy 动态代理
6. @MapperScan 与@Mapper 区别
@MapperScan 是 Spring-Mybatis 的注解
@Mapper 是 Mybatis 的注解
SpringBoot 中如果没有包路径配置,单单只加@Mapper 也可以生效。但 Spring 中必须要有包扫描路径,@Mapper 只最为标记使用,配合 @MapperScan(value = "com.bjpowernode.mapper", annotationClass = Mapper.class)
进行过滤
❕
7. 实战经验
8. 参考与感谢
8.1. 图灵司马
Spring 整合 MyBatis 源码真谛、springIOC 源码剖析、@Import 注解作用详解、FactoryBean 与的区别
https://www.bilibili.com/video/BV1uJ41117A7?p=4&spm_id_from=pageDriver
8.2. 动力节点
https://www.bilibili.com/video/BV1uF411L73Q?p=98&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204
[[Spring整合MyBatis源码深度剖析.docx]]
其他已存百度网盘 - 动力节点
8.2.1. 代码示例
[[pages/002-schdule/001-Arch/001-Subject/013-DemoCode/spring-framework/spring-z-mybatis-2/src/main/java/com/bjpowernode/config/MyConfig.java]]
8.3. 网络笔记
https://www.cnblogs.com/hei12138/p/mybatis-spring.html