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

实现了 InitializingBeanafterPropertiesSet() 方法

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 方法,调用 MapperProxyFactorynewInstance 生成对应的 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