1. 环境准备 ConfigurationClassPostProcessor

https://www.processon.com/diagraming/63e4bfa27c423a1934f127a5
❕%%
▶2.🏡⭐️◼️CCPP 是容器环境最重要的准备工作 ?🔜MSTM📝 只有 ConfigurationClassPostProcessor 在 this() 中注册完 BD,在 refresh() 中的第 5 步 invokeBeanFactoryPostProcessors 中创建完 Bean 并执行 postProcessBeanDefinitionRegistry 方法,其他注解才能陆续的被注册 BD 被创建 Bean。◼️⭐️-point-20230220-1406%%

1.1. 注册 BPP 的 BD

image.png

image.png
image.png

1.2. 创建 BPP 的 Bean

image.png

2. 注册 MapperScannerConfigurer

2.1. 注册 MapperScannerConfigurer BD

2.1.1. @Import(MapperScannerRegistrar)

@Import 使用方法

Spring-1、基本原理

❕%%
▶3.🏡⭐️◼️@Import 的使用方式有 3 种,1. 直接导入单个 class,2. 导入一个全限定名数组字符串,3. 导入一个实现了 ImportBeanDefinitionRegistrar 的 registrar 类,在该类里手动注入一个 BD,这种方式会在 ConfigurationClassBeanDefinitionReader 中的 loadBeanDefinitionFromRegistrar 方法中尽心解析和注册◼️⭐️-point-20230220-1428%%

image.png

可以发现@MapperScan 注解中使用的是@Import 的第 2 种用法,导入一个实现了 ImportBeanDefinitionRegistrar 的类 MapperScannerRegistrar.class
image-20210919193638832

@MapperScan 注解的属性不为空,说明配置了这个注解,就会调用分支里的重写的方法 registerBeanDefinitions
image-20210919193743332

2.1.2. 建造者构造 MapperScannerConfigurer BD

在 MapperScannerRegistrar.class 的方法 registerBeanDefinitions 中用创建 bean 定义构造器 BeanDefinitionBuilder,并通过构造器构建出 MapperScannerConfigurer 的 bean 定义
应用到了设计模式:建造者设计模式
image-20210919193901449

然后将构建的 MapperScannerConfigurer 的 bean 定义注册到容器中
image-20210919194157619

image-20210919194255778

2.1.3. 注册流程 loadBeanDefinitionsFromRegistrars

ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry
this.reader.loadBeanDefinitions
loadBeanDefinitionsForConfigurationClass
loadBeanDefinitionsFromRegistrars

image.png

ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry 走完除了 Mapper 接口对应的 BD,其他的包括 MapperScannerConfigurer 在内都已注册 BD。接下来走 MapperScannerConfigurer 的 postProcessBeanDefinitionRegistry 才开始注册 Mapper 的 BD。❕%%
▶4.🏡⭐️◼️ConfigurationClassPostProcessor 的 postProcessBeanDefinitionRegistry 方法走完后的现状 ?🔜MSTM📝 MapperScannerConfigurer BD 已经注册好◼️⭐️-point-20230220-1452%%

image.png

2.2. 创建 MapperScannerConfigurer Bean

image.png

因为 MapperScannerConfigurer 是 BeanDefinitionRegistryPostProcessor 的实现类

2.3. MapperScannerConfigurer 继承关系⭐️🔴

image.png

3. 扫描 Mapper 接口成为 BD

上面一步注册了MapperScannerConfigurer 的 BD 信息,接下来看 MapperScannerConfigurer 的作用。
类 MapperScannerConfigurer 实现了 BeanDefinitionRegistryPostProcessor 接口中的 postProcessBeanDefinitionRegistry 方法,这个方法也可以为容器中注册 bean 定义

3.1. 触发时机⭐️🔴

MapperScannerConfigurer.postProcessBeanDefinitionRegistry

image.png

3.2. 扫描原理 (ClassPathMapperScanner)⭐️🔴

https://www.processon.com/diagraming/63f1d3dea600c6676369dd8f

image.png

❕%%
»5.🏡⭐️◼️重写了 2 个方法:isCandidateComponent 和 processBeanDefinitions 方法◼️⭐️-point-20230220-1507%%

image.png

实现 BeanDefinitionRegistryPostProcessor 是IOC 的一个扩展点(Spring-2、IOC)可以自主的在 bean 的概念态到定义态的过程中,往容器里注入 bean 定义
image-20210920092547247

BeanDefinitionRegistryPostProcessor 中的 postProcessBeanDefinitionRegistry() 方法会在 IOC 容器中注册@MapperScan 扫描到的 mapper 包下的 bean 定义
image-20210920094515529

image-20210920095742460

3.2.1. 设置过滤规则

image.png

image.png

https://www.bilibili.com/video/BV1uF411L73Q?p=102&spm_id_from=pageDriver&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204

3.2.2. 设置 BD 判断规则

3.2.2.1. Spring 默认的判断规则

image.png

 isIndependent 判断当前 BeanDefinition 对应的类是否是独立类(顶级类,静态内部类)**顶层的类 (独立类)**(没有父类或静态内部类) ❕%%
▶1.🏡⭐️◼️顶级类的概念 ?🔜MSTM📝 再没有父类或者是静态内部类◼️⭐️-point-20230222-0701%%

 isConcrete 是否非接口非抽象类
 hasAnnotatedMethods(Lookup.class.getName()) 是否有包含@Lookup 注解的方法
 
 成立条件:首先必须是独立类,其次要么是非抽象类非接口,要么是抽象类但是有@Lookup 注解的方法

3.2.2.2. Mybatis 重写的判断规则

image.png
扫描独立的接口

image.png

3.2.3. 扫描过程

image.png
image.png

4. mapper 实例化 (mapper 是接口原本无法实例化)

4.1. processBeanDefinitions(偷梁换柱)⭐️🔴

ClassPathMapperScanner将接口类型的 mapper 的 definition 类型改为 MapperFactoryBean
image.png


4.2. MapperFactoryBean⭐️🔴


❕%%
▶6.🏡⭐️◼️为什么要用 factoryBean ?🔜MSTM📝 因为我们要在使用 Mapper 就必须获取 Bean,但是 Mapper 是接口无法实例化,所以通过使用动态代理类来使用 Mapper 接口中的方法。此时容器无法自动管理和装配我们需要的动态代理,所以需要告诉容器要返回给我们什么,那么 factoryBean 可以完成这种功能。在 getObject 方法中返回创建的动态代理◼️⭐️-point-20230220-1524%%

4.3. JDK 动态代理接口设置

image-20210920112818543

image.png

指定生成代理类时使用的是有参构造器,并且入参是我们的 mapper 接口

4.4. 修改注入模式 -byType⭐️🔴

image.png

4.4.1. 原因 1 非空判断

image.png

是因为 MapperFactoryBean 继承于 SqlSessionDaoSupport,SqlSessionDaoSupport 里有个属性 sqlSessionTemplate 有非空断言。因为属性上没有@Autowired,默认情况下是无法自动装配的。所以要改成按类型,在 IOC 里查找到之后,调用 set 方法,进行赋值就可以完成自动装配

Spring 中 AbstractBeanDefinition.AUTOWIRE_BY_TYPE 默认设置为 0,需要加@Autowired 才能完成自动装配。如果没有显示加@Autowired,那么就必须修改自动注入模式。
image.png
❕%%
▶9.🏡⭐️◼️为什么要修改自动注入模式 ?🔜MSTM📝 因为 Spring 默认值是 0,要完成自动注入必须显示的加@Autowired。而我们的 MapperFactoryBean 是通过 FactoryBean 获取到的,没法加@Autowired,同时又继承自 SqlSessionSupport,里面有个 notnull 断言的 SqlSessionTemplate。我们在使用声明式事务时配置的 sqlSessionFactory,在 setter 方法中,设置了 SQLSessionTemplate,巧妙的解决了非空断言的问题。◼️⭐️-point-20230220-2221%%

4.4.2. 原因 2 不易报错

image-20210919190639402

sqlSessionTemplate 必须赋值,改为 by_type 赋值不容易出错

5. mapper 使用逻辑

5.1. 生成动态代理并缓存 (SqlSessionFactoryBean)

5.1.1. 流程图

https://www.processon.com/diagraming/63f04e03e39b2d4955a8c9cc

image.png

5.1.2. 缓存 mapper 接口信息

5.1.2.1. SqlSessionFactoryBean 继承关系

image.png

实现了 InitializingBean 的 afterPropertiesSet() 方法

5.1.2.2. 缓存时机

image.png

5.1.3. 生成动态代理⭐️🔴⭐️🔴

  1. 由 MapperScannerConfigurer 来扫描对应的 Mapper 接口的 BD 信息,并将类型替换为 MapperFactoryBean。
  2. 由 SqlSessionFactoryBean 来扫描 mapper 接口并配置与 MapperProxyFactory 的对应关系,保存到 mapperRegistry 中的 knownMappers Map 中。
  3. 自动装配 Mapper 时,从容器获取 mapper 但由于实现了 FactoryBean 接口,所以由 getObject 方法生成。在 getObjects 方法中,最终调用到 mapperRegistry 中的 getMapper 方法,调用 MapperProxyFactory 的 newInstance 生成对应的 MapperProxy 即 Mapper 接口的动态代理。 ❕%%
    ▶8.🏡⭐️◼️最终得到的是 MapperFactoryBean 吗 ?🔜MSTM📝 当然不是,而是 MapperProxy◼️⭐️-point-20230220-1846%%

5.2. 使用动态代理

当从 Spring 中获取 Mapper 接口时,将会调用对应的 MapperFactoryBean 的 getObjects 方法,该方法返回值即为对应的 MapperProxyFactory 创建的 MapperProxy 动态代理
image-20210920115553380
image-20210920120211908
image-20210920120338745
image-20210920120418425

6. @MapperScan 与@Mapper 区别

@MapperScan 是 Spring-Mybatis 的注解
@Mapper 是 Mybatis 的注解

image.png

SpringBoot 中如果没有包路径配置,单单只加@Mapper 也可以生效。但 Spring 中必须要有包扫描路径,@Mapper 只最为标记使用,配合 @MapperScan(value = "com.bjpowernode.mapper", annotationClass = Mapper.class) 进行过滤
❕%%
▶5.🏡⭐️◼️Spring 与 SpringBoot 配置上的不同 ?🔜MSTM📝 ◼️⭐️-point-20230226-1832%%

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

8.4. 内部类

https://blog.csdn.net/f641385712/article/details/106451554