spring-mybatis 基于springboot整合过
2022-04-25 21:04:11 10 举报
AI智能生成
登录查看完整内容
spring mybatis 整合
作者其他创作
大纲/内容
mybatis-spring-boot-starter:2.0.0
spring-boot-starter-parent:2.3.12.RELEASE
1.说明
1.必然是要扫描业务Mapper接口
2.那么如何扫描? 熟悉spring(springboot) 原理的 必然知道,一般都是通过后置处理器来完成的
1.实现bean定义的接口是:BeanDefinitionRegistryPostProcessor
MapperScannerConfigurer
1.在纯spring集成mybatis中,我们经常看到这样的配置
1.看这个也是可以的,也很容易理解工作它的流程
2.他是可配置的,你可以配置是否扫描@Mapper注解,或者只配置包名 都可以的
2.果不其然,可以找到 MapperScannerConfigurer
3.要想创建对象,必须在此之前要先去创建BeanDefinition 对象,那么这就是突破口
MybatisAutoConfiguration
2.springboot 中要想使用某一个功能,最先想到的就是其 xxxConfiguration 配置类
1.这个类将扫描 @Mapper 注解标注的接口,完成bean定义
1.不难发现,他扫描的是springboot 主配置类的包,注意:不是@ComponentScan 注解指定的包
2.@MapperScan 注解后面再说
2.这个类上面有注释,翻译如下:这将扫描与Spring Boot相同的基本包。如果您想要更强大的功能,您可以显式地使用 @MapperScan 注解
1.导入了 AutoConfiguredMapperScannerRegistrar
2.注意,这个类要想向容器中注册组件,其前提条件是容器中没有 MapperFactoryBean,这个后面在说
3.由 MybatisAutoConfiguration 配置类导入必要的组件,完成bean 的定义功能
4.既然在springboot 中 MapperSannerConfigurer 是不工作的,那么怎么扫描我们的业务接口呢?
2.分析思路
0.这个类实现了 ImportBeanDefinitionRegistrar 接口,其作用就是给容器注册组件的,我发现自动全注解开发后,这个类经常配合@Import 注解使用
spring boot bean 定义过程
1.springboot 的 bean 定义过程,可以参考我画的图
2.至于这个类是怎么生效的,方法是何时调用的,后面再说,这里先详细讲述它的工作流程(3)
1.首先根据这个类的注释知道,扫描的包路径 默认就是springboot 主配置类的包路径(注意:和@ComponentScan 注解无关)
这个类在new的时候,其构造器内部会指定默认的扫描规则,就是去扫描@Component 注解
1.这个类就是spring 扫描 @Componet 注解的,功能性非常强大
1.这个类功能也很强大
扫描规则
2.它可以指定规则或者排除规则,比如spring 指定了扫描 @Componet 注解的类
比如mybatis 指定了只有接口才符合,最终才会创建bean定义对象
是否符合候选条件
3.它还可以指定是否符合帅选条件
2.这个类继承了 ClassPathScanningCandidateComponentProvider
3. public Set<BeanDefinitionHolder> doScan(String... basePackages) 这个方法是重中之重
1.这个类继承了 ClassPathBeanDefinitionScanner 类
1.指定了注解类是 @Mapper
3.将上面的过滤器添加到 ClassPathScanningCandidateComponentProvider 类中的 includeFilters 属性中,这样最后面扫描的时候,就只选择 @Mapper 注解的bean作为候选
2.添加扫描的过滤规则
1.扫描这个包下的所有的class文件
1.遍历排除的组件过滤器:TypeFilter tf : this.excludeFilters
1.前面我在这里设置了 @Mapper 注解类
然后创建 ScannedGenericBeanDefinition 对象,然后来到第3步
2.如何类上有 @Mapper 注解,表示可以标注为:候选组件
2.遍历包含的组件过滤器:TypeFilter tf : this.includeFilters
2.判断是否为候选的组件
1.这里调用的方法和上面的方法名字是一样的,只是参数不一样,执行的时机也不一样
如果符合,则将第2步骤创建的 ScannedGenericBeanDefinition 对象保存到 Set 集合中返回
2.判断是否为候选组件,比如 mybatis 重写了这个方法,判断类只有是接口才符合候选
3.继续判断是否为候选的组件
4.遍历 3中返回的 Set<BeanDefinition> 集合,将每个 bean 定义对象保存到 font color=\"#f44336\
1.调用父类 ClassPathBeanDefinitionScanner 类 的 doScan(String... basePackages) 方法
1.如果集合不为empty, 则开始处理
为什么这样做?
2.这里有一个最重要的改动,就是获取 BeanDefinition 对象后,将内部的 beanClass 从原来的 com.qiuguan.test.mapper.xxxMapper 接口类变成了现在的 org.mybatis.spring.mapper.MapperFactoryBean, 所以实际上font color=\"#f44336\
2.步骤1执行完成后,返回一个 Set<BeanDefinitionHolder> 集合(就理解为 BeanDefinition 集合就可以)
3.至此扫描 @Mapper 注解就结束了,将扫描到的接口,添加到bean定义中,注册到 BeanDefinitionRegistry 对象中
3.开始扫描
2.创建了ClassPathMapperScanner 对象
继承 ClassPathBeanDefinitionScanner 类
1.mybaits 的整合让我知道了如果我们自定义包扫描应该怎么处理
添加 TypeFilter
2.如何指定候选规则或者排除规则
这里返回true,才会去创建beanDefinition 对象
重写 ClassPathScanningCandidateComponentProvider 类中的 isCandidateComponent(AnnotatedBeanDefinition beanDefinition) 方法
3.如何指定最终的候选条件
4.如果我们也想像mybatis 一样,去扫描自己的包,所以自己的注解,就可以遵照上面的步骤
学习理解
3.AutoConfiguredMapperScannerRegistrar#registerBeanDefinitions
1.springboot 启动时,他会去创建 ClassPathBeanDefinitionScanner对象,注意,他不是交给spring管理的,他是new出来的
2.它会指定去扫描标注了 @Componet 注解的组件
3.然后mybatis 的 ClassPathMapperScanner 对象它也是new出来的,由于有继承关系,那么ClassPathBeanDefinitionScanner,ClassPathScanningCandidateComponentProvider 都会去创建新的对象,都是new出来的;通过debug 我发现,像 ClassPathBeanDefinitionScanner 对象会有很多次去创建(就是去 new)
顺便说一句
4.上面3的过程非常具有学习意义,对于我们如何集成spring具有很好的学习经验
3.AutoConfiguredMapperScannerRegistrar 如何完成bean的定义
AppConfig
1.我添加到普通配置类 AppConfig 上
1.实现了 ImportBeanDefinitionRegistrar 接口,其作用就是注册组件
1.和 自动配置类导入的 AutoConfiguredMapperScannerRegistrar 本质时一样的
1.首先创建 ClassPathMapperScanner 对象(和前面一样)
1.是否有 annotationClass 属性,指定扫描规则时会用到,就是去指定要去扫描的注解,可以为空,如果我想就让它去扫描 @Mapper 注解,那么就在这里指定即可
2.以及其他属性
3.
2.解析 @MapperScan 注解的属性
1.如果 font color=\"#2196f3\
2.如果 markerInterface 属性 指定了接口,则将该接口作为扫描的规则,注意:千万不要指定这个属性
3.如果都没有指定,则该包下的所有类都时符合扫描规则的
4.以上规则,三选一生效
3.注册扫描或者排除组件的规则
1.调用父类 ClassPathBeanDefinitionScanner 的 doScan(String... basePackages) 方法
2.同 AutoConfiguredMapperScannerRegistrar 的扫描过程一模一样
4.开始扫描
2. 获取 @MapperScan 注解
2.MapperScannerRegistrar#registerBeanDefinitions 方法解析
1.给容器导入了 MapperScannerRegistrar
4.@MapperScan 注解
1.如果指定了@MapperScan 注解(注意:没有指定 annotationClass 属性),那么 @Mapper 实际上是没用的
通过 mybaits 自动配置类导入的
生效条件
它生效的前提是没有 MapperFactoryBean
AutoConfiguredMapperScannerRegistrar
通过 @MapperScan 注解导入的
MapperScannerRegistrar
2.根本原因是这两个导入的配置类,只有一个会生效
1.看到这个是不是很奇怪,为什么是二者如何选择?
1.首先,在我们的自己的配上类上添加 @MapperScan(basePackages = \"com.qiuguan.test.mapper\") 注解
1.它继承了 BeanDefinitionRegistryPostProcessor
2.解析配置类组件,将组合条件的bean 保存到 BeanDefinitionRegistry
1.既然是分析bean定义,那么还是以bean 定义处理器为主,找到配置类处理器:ConfigurationClassPostProcessor
1.这个方法就是在 spring 容器刷新的 invokeBeanFactoryPostProcessors() 方法中调用,遍历所有的处理器,调用方法
BeanFactoryPostProcessor
BeanPostProcessor
AopInfrastructureBean
EventListenerFactory
根据 beanName 获取 BeanDefinition 对象,如果实现这些接口,则不满足成为候选配置类
根据 beanName 获取 BeanDefinition 对象,看是否有 @Configuration 注解,如果有,则添加到Set集合中
2.判断是否为候选的配置类
1.这个方法非常非常复杂,但是这里也会去创建BeanDefiniton 对象,但是只会去创建 @ComponentScan 注解扫描到的,其他的像 @Import 注解导入的,@Bean 导入的,@ImportSource 导入的等等 都不会在这里创建BeanDefinition 对象,而是要下放到步骤2中
2.
1.ConfigurationClassParser#parse
2.ConfigurationClassBeanDefinitionReader#loadBeanDefinitions
1.解析主要有两个方法
0.请先看下 1- 9的总体过程,然后在看每一步骤的细节
说明,在看下面的内容时,请先看 1-9 的总体内容,细节先不要看,然后1-9 看完了,在从头一点一点看细节
刚开始进来,第一次执行,这个配置类就是主配置类,所以先看下主配置类执行1-9的过程
由谁导入的
1.图1
imortedBy
2.图2
3.图3
当前配置类是由谁导入的
Set<ConfigurationClass> importedBy
将 @ImportResource 注解导入的xml文件信息 保存到当前配置类中
将 @Import 注解导入的实现了 ImportBeanDefinitionRegistrar 接口的类 保存到当前配置类中
将 @Bean 注解标注的方法,保存到当前配置类中
Set<BeanMethod> beanMethods
ConfigurationClass 几个重要属性
parse
1.将当前配置类封装成 ConfigurationClass类(new ConfigurationClass() ),然后递归地处理配置类及其超类层次结构, do-while 结构
@SpringBootApplication —> @SpringBootConfiguration —> @Configuration —> @Component 注解
1.遍历所有的内部类
1.其作用就是继续执行1-9的步骤
@Configuration 注解上也有 @Component
不难发现 @Component 注解标注的内部类都会成为配置类
标注了 @Component 注解
这个不做说明,一般内部类不会标注这个注解
标注了 @ComponentScan 注解
标注了 @Import 注解
标注了 @ImportResource 注解
或者是类中有标注了@Bean 注解的方法
2.成为候选类的条件
3.一旦成为候选配置类,则将其添加到List集合中
2.判断内部类是否为候选配置类
注意:假如内部类标注了@Component 注解,那么在这里也当作是配置类,然后创建 ConfigurationClass 对象进行封装
3.如果这里的候选配置类集合不为空,则遍历每一个候选配置类,然后执行 1-9 中的过程(每次都是 new 新的 ConfigurationClass 对象)
4.说明:这里如果执行了,说明刚开始的主配置类解析就会停下来,让这些普通的候选配置类先去执行,当这个都执行完了(执行完1-9过程),则继续执行主配置类的第3步骤的解析
1.如果有内部成员类
2.如果没有内部成员类,则执行接下来的步骤3
很明显,主配置类是满足条件的,所以要 递归的处理其内部成员类
2.判断配置类是否有 @Component 注解
1.如果有,则解析这个注解,这个注解是引入 自定义的 xxx.properties 是属性文件,添加到环境变量中
2.如果没有,则执行接下来的步骤4
3.判断是否有 @PropertySource 注解
1.如果有,则解析这个注解
在new这个对象时,其构造器内部做了一件很重要的事情:就是指定了扫描规则,就是去扫描@Component 注解
1.其内部 会创建 ClassPathBeanDefinitionScanner 对象,这个是spring扫描工作中一个非常重要的对象(mybatis 扫描继承了这个)
2.解析 @ComponentScan 注解的属性,其中就有,如果basePackages 属性为空,则取当前配置类所在的包为基准包路径
1.前面在这里设置了 @Component 注解类
2.如何类上有 @Component 注解,表示可以标注为:候选组件
2.判断是否为候选组件,如果标注了@Component 注解,或者是font color=\"#fbc02d\
只要 @ComponentScan 注解扫描到类,才会在这里就保存到 BeanDefinitionRegistry 对象中,其余的都要放到解析过程2中的 ConfigurationClassBeanDefinitionReader#loadBeanDefinitions
1.检查是否为候选配置类
1.标注了 @Configuration 注解
2.标有基本注解
3.标有 @Bean 注解的方法
2.候选配置类的条件
3.如果满足条件,则将当前配置类(这里就这么理解吧,毕竟@Component 注解标注的类在这里都算是配置类)封装到配置类ConfigurationClass 中,然后执行大步骤1中的 do-while 循环,然后遍历每一个类,然后执行1-9步骤
5.遍历 4中返回的 Set<BeanDefinitionHolder> 集合
3.调用 ClassPathBeanDefinitionScanner 对象的 doScan(basePackages) 方法开始扫描
2.通过 ComponentScanAnnotationParser 对象开始解析
2.这个其实是一定有的,因为 @SpringBootApplication 注解上标注了,解析的时候,判断basePackages 为空,则使用主配置类所在的包
3.解析完成后,则执行接下来的步骤5
4. 判断是否有 @ComponentScan 注解
1.递归获取当前配置配置类上的@Import 注解,如果没有,则返回,执行步骤6
这个没有太细看,我用的不是很多
1.如果是 ImportSelector 类型的,则处理导入
1.注意:它是保存到当前配置属性中,并没有去扫描去创建 BeanDefinition 对象
然后将 MapperScannerRegistrar 类放到当前配置类的 importBeanDefinitionRegistrars 属性中
2.我一般是在普通配置类上添加了 @MapperScan 注解,将会导入 MapperScannerRegistrar 类
2.如果是 ImportBeanDefinitionRegistrar 类型的,则保存到当前配置类 ConfigurationClass (他就是对当前配置类的进一步封装),放到 importBeanDefinitionRegistrars 这个Map 属性中, 每一个配置类都会有对应的配置封装类 ConfigurationClass
3.如果1,2都不符合,则将其当作配置类来处理;然后将这个导入的类当作配置类,在去重复1-9 过程
2.如果有,则遍历所有 @Import 导入的类
3.上面的步骤2执行完,开始下面的步骤6
5.处理 @Import 注解
1.这个注解,就是导入以前spring 的xml文件
2.如果有,则将配置文件信息把保存到配置类ConfigurationClass (他就是对当前配置类的进一步封装)的 importedResources 这个Map属性中
3.注意:它是保存到当前配置属性中,并没有去扫描去创建 BeanDefinition 对象
4.接下来执行步骤7
6.处理 @ImportResource 注解
1.如果当前配置类中有 @Bean 注解标注的方法,则遍历所有的@Bean方法,然后将其放到当前配置类ConfigurationClass (他就是对当前配置类的进一步封装)的 beanMethods 这个 Set 属性中,并且会将方法封装成 BeanMethod 对象进行保存
2.注意:他也是保存,并没有去扫描创建 BeanDefinition 对象
3.接下来执行步骤8
7.解析 @Bean 注解
1.看当前配置类是否实现了接口,接口中是否有 @Bean 注解的默认方法,如果有,则解析 @Bean注解,将其放到当前配置类ConfigurationClass 的 beanMethods 这个Set属性中
3.接下来执行步骤9
8.解析 当前配置类实现的接口
1.如果有,则直接返回,其实这里的返回就是继续执行 开始的 do-while 循环,在将上面标注的 1-9 执行一遍
3.接下来执行步骤10,不可忽略!!!!
9.解析当前类的父类
1.一旦当前配置类解析过程结束,注意:这个结束表示当前配置类已经执行完了1-9的过程,最后返回了 null(这个结束,只是当前配置类解析结束了,以上1-9的过程实际上是非常复杂的,每一步骤的细节可能仍然谁重复1-9的过程,这样很不好理解,所以我把 1-9 的过程单独拿出来说明,这样比较好理解
1.font color=\"#9c27b0\
这个ConfigurationClass 类的 importBeanDefinitionRegistrars 属性 中,保存了 @Import 注解导入的 MapperScannerRegistrar 类
请参考图片
font color=\"#f44336\
这个就是@Componet 注解标注的(@Controller 本质也是@Component)
这个类是 @Import 注解导入的,但是这个类没有实现 ImportSelector, ImportBeanDefinitionRegistrar 这两个接口
说明:index = 0 和 1 是没有确定顺序的,先解析谁,谁就在第一个位置,但是主配置类,一定是在用户自定义配置类中的最后一个
1.首先LinkedHashMap 首先存放的是 @ComponentScan 注解扫描到的我们自定义的业务配置类(比如 @Configuration ,@Component 等),以及是 @Import 注解导入的类但是这个类没有实现 ImportSelector, ImportBeanDefinitionRegistrar 这两个接口。这里有点意思,可能扫描到的一个普通@Component 组件,在这里也会当作配置类保存起来
........
这个ConfigurationClass 类的 importBeanDefinitionRegistrars 属性 中,保存了 @Import 注解导入的 AutoConfiguredMapperScannerRegistrar 类
.........
2.其次存放的就是spring 自动配置导入一些配置类
3.所以说,我们用户导入或写的配置类,要比spring自动配置导入的要靠前,越靠前在 this.reader.loadBeanDefinitions 中就先去解析,请看后面的解析过程第2大步骤
2.它具体存储了什么?
2.然后将配置类放到 ConfigurationClassParser 类中的 configurationClasses 这个Map属性中,这个Map的key和value 都是前面一直提到的 ConfigurationClass 类,它是每一个配置类的封装,然后这里面保存着1-9中保存进来的内容(绿色部分),然后留着接下来的第二大步骤去进一步解析 (this.reader.loadBeanDefinitions)
10.整个解析过程结束
1.只会将 @ComponentScan 注解扫描到类以及 @Import 注解导入的且没有实现 ImportBeanDefinitionRegistrar 和 ImportSelector 接口的类 保存到 BeanDefinitionRegistry 中,其他的不会去创建 BeanDefinition 对象
2.这里的配置类,不是在我们习惯意义上的 @Configuration 标注的类,而是 @ComponentScan 注解扫描到的,比如 font color=\"#fbc02d\
配置类解析
2.将每个配置类保存到 ConfigurationClassParser 类中的 configurationClasses 这个Map属性中
1.上面的 parse 方法执行完
1.将 font color=\"#9c27b0\
1.前面说了,配置类是有顺序的,用户引入进来的排在前面,先去解析,即:@MapperScan 注解导入的MapperScannerRegistrar 先去解析,首先它在前面先去解析,其次它没有限制条件,所以一定会解析成功
1.MapperFactoryBean 对象怎么来的?
创建MapperFactoryBean 对象时,会注册我们的业务Mapper接口,选择的是有参数构造器
BeanFactory对象何时创建
2.在哪里创建的对象?
2.当mybatis 的配置类来到这里时,因为@MapperScan 注解导入的 类已经执行过了,有了 font color=\"#d32f2f\
1.如果符合跳过条件,则直接返回,进行下一次的遍历
2.如果不符合,则执行步骤2
1.判断是否需要跳过,就是标注了 @Condition 条件
1.就是前面开始我说的 ConfigurationClass 几个重要属性中的 importedBy 属性;请回看
@Import 导入的随便一个类也是要去创建BeanDefinition 对象
AppConfig 导入 SingletonImport
2.我通过 @Import 注解导入的自定义 SingletonImport 类(这个类没有继承,没有实现,没有属性和方法),这个类是由AppConfig 导入的,所以它符合条件,于是创建 SingletonImport 类的 BeanDefinition 对象,然后保存到 BeanDefinitionRegistry 类中
2.解析 importedBy 属性;判断是否是导入的
1.就是前面开始我说的 ConfigurationClass 几个重要属性中的 beanMethods 属性
2.解析@Bean注解的方法,然后根据返回值创建 BeanDefinition 对象,然后保存到 BeanDefinitionRegistry 中
3.解析 beanMethods 属性;获取所有的 @Bean 注解的方法进行遍历
1.就是前面开始我说的 ConfigurationClass 几个重要属性中的 importedResources 属性
2.以前没有注解的时候,都是通过spring的xml去创建对象,通过这个注解,就是将xml导入进来,去解析其中的<bean> 标签,然后创建BeanDefinition 对象,然后保存到 BeanDefinitionRegistry 类中
4.解析 importedResources 属性;获取 @ImportSource 导入的 xml文件
1.就是前面开始我说的 ConfigurationClass 几个重要属性中的 importBeanDefinitionRegistrars 属性
1. @MapperScan 注解注入了 MapperScannerRegistrar 类,这里就要去执行方法,去获取 @MapperScan 注解的信息,去扫描 指定包下的接口(如果没有 annotationClass 属性,那么它默认扫描的就是指定包下的所有类,然后将是接口的类保存到 BeanDefinition 对象中
@MapperScan 注解注入了 MapperScannerRegistrar 类,在前面,所以他先去解析
但是当他去解析的时候,第一步会因为有跳过条件,所以它不会在去执行方法了,直接退出,进行下一次循环
mybatis 自动配置类导入的 AutoConfiguredMapperScannerRegistrar, 在后面,它后面再去解析
2.前面我说了,ConfigurationClass 是按顺序存储在Map中,用户指定的在前面,先去解析,spring自动导入的在后面,后面解析
这个MapperFactoryBean对象
3.在@MapperScan 执行完成后,会存在一个 MapperFactoryBean 对象,mybatis 自动配置类的 AutoConfiguredMapperScannerRegistrar 有一个生效条件就是 @ConditionalOnMissingBean(MapperFactoryBean.class); 所以 mybatis 自动配置类,在执行第1步骤的时候,会因为符合跳过条件,从而不进行解析
2.遍历所有实现了ImportBeanDefinitionRegistrar 接口的类,然后调用 registerBeanDefinitions 方法,这个方法内部就是注册组件的逻辑
5.解析 importBeanDefinitionRegistrars 属性;获取 @Import 导入的实现了 ImportBeanDefinitionRegistrar 接口的类
2.遍历所有的 ConfigurationClass
解析配置类
2.开始解析 configurationClasses 属性
2.this.reader.loadBeanDefinitions
2.解析过程
3.创建 ConfigurationClassParser 对象(new出来的)用来解析每一个配置类(实际上这里就一个主配置类)
2.详细解析这个方法
2. ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
2.其次分析springboot 的 bean 定义过程
2.原理探究
1.实际上 @MapperScan 注解扫描的并不是 @Mapper 注解
如果就想让@MapperScan 注解去扫描@Mapper注解,则在属性 annotationClass 指定为 @Mapper 类名
2.如果使用了@MapperScan 注解,只要指定好包名,那么@Mapper注解就可以忽略了
4.所以:最好使用@MapperScan 注解
3.结论
5.@Mapper 和 @MapperScan 注解如何选择?
1.前面讲述了 spring 如何 集成 mybatis 扫描 Mapper 接口,从而创建 BeanDefinition 对象,等待后面去创建对象
SqlSessionFactory
1.如何创建对象,创建哪个对象?
是一个工厂Bean
implements FactoryBean<SqlSessionFactory>
1.内部首先创建 SqlSessionFactoryBean 对象(new出来的)
可以没有
1.从springboot 的 属性文件中获取mybatis 的全局配置文件位置
2.创建 Configuration 对象,保存到属性中,这个 Configuration 对象是mybayis 中一个重量级的配置类
3.获取指定的 mapper接口的xml配置文件位置
4.以及一些其他配置,比如别名等等,这些不重要
3.给 SqlSessionFactoryBean 设置属性
1.这个属性,就是mybatis 的全局配置文件
1.开始解析标签,比如 font color=\"#388e3c\
1.如果mybatis 全局配置文件中没有配置 <mappers> 标签,则直接跳过;或者有 <mappers> 标签但是没有 <mapper>标签也同样跳过
不要加 classpath: 不然会报错找不到这个文件
1.例如:<mapper resource=\"mapper/UserMapper.xml\"/>
2.首先判断这个资源是否加载过了,按照资源名resource=\"mapper/UserMapper.xml\" 判断
/mapper/sql
/mapper/resultMap
2.在解析 <mapper> 标签下的一些独立标签,比如
一个标签对应一个 MappedStatement 对象
1.将标签的所有信息,包括sql 全部封装到 MappedStatement 对象中
2.然后将 MappedStatement 对象保存到 Configuration 对象的 mappedStatements 属性中(是一个Map)
3.在解析 <mapper> 标签下的 CRUD 标签
3.如果没有加载过,则开始解析
值就是 mapper/UserMapper.xml
4.将上面解析完的资源保存到 Configuration 类的 loadedResources 属性,与步骤2换成一对
1.如果不是接口,则直接跳出,不进行注册
key是接口Class对象
value 是 MapperProxyFactory 对象
font color=\"#4caf50\
2.将当前接口保存到 MapperRegistry 对象的 knownMappers 的Map属性中
内部是调用 MapperRegistry 对象的 addMapper 方法
2.如果该接口没有注册过,则调用 Configuration 对象的 addMapper 方法
1.他是去解析接口的注解信息
注意:如果xml和注解重复了,那么这里就会报错,启动失败
2.它和解析xml是一样的,也会创建 MappedStatement 对象,并将注册到 Configuration 类的 mapperRegistry 属性中
3.我觉得执行完上面的步骤2就结束了,但是其实没有,它创建了 MapperAnnotationBuilder 对象
5.将 Mapper 接口注册到 Configuration 类的 mapperRegistry 属性中
2.如果配置了<mappers> 标签,并且里面有 <mapper> 标签,则遍历解析每个 <mapper> 标签
2.重点说 <mappers> 标签
2.如果配置了,则解析
3.如果没有配置,则执行第2步
1.解析 mybatis 的 configLocation 属性(基于springboot 进行配置的)
1.这个就是我们的业务 Mapper xml文件
基于mybaits 全局配置的解析出来的是: mapper/UserMapper.xml
基于springboot 全局配置解析出来的是:[mapper/UserMapper.xml]
所以,虽然是同一个文件,但是判断出现了问题,所以还是会去解析
1.首先它执行的 1.2.2.2.2 资源加载判断
但是当保存 MappedStatement 对象时,由于key已经存在了,所以报错
解析过程同1.2.2.2.3 一样
1.如果mybatis 全局配置文件中也配置了,就是我上面说的情况,这里同样会去解析
2.所以,1.注解,2.mybatis 全局配置文件制定 mapper 接口,3.springboot 制定mapper 接口,三者要选一,不然报错
2.解析 Mapper.xml 文件
3.如果没有配置,则执行步骤3
2.解析 mybatis 的 mapperLocations 属性(基于springboot进行配置的)
DefaultSqlSessionFactory
3.最终就是返回一个 SqlSessionFactory
关注第7章节
checkDaoConfig()
重点关注 MapperFactoryBean 对象的初始化
4.然后调用 b style=\
2.自动配置类中有一个 font color=\"#e65100\
这个对象内部有个 sqlSessionProxy 属性,是 SqlSession 代理对象,这里用到了代理
1直接 new SqlSessionTemplate() 创建对象
3.自动配置类中有一个 sqlSessionTemplate() 方法,就是去创建 SqlSessionTemplate 对象,继续分析
2.应该从哪里分析
2.接下里就是创建对象
SqlSessionFactoryBean
implements FactoryBean<T>
MapperFactoryBean
3.mybatis 里面的重要的两个工厂对象
6.如何解析配置文件和Mapper接口?
key = userMapper
value = GenericBeanDefinition; 其中 GenericBeanDefinition 的 beanClass 属性修改成了 MapperFactoryBean.class
其中就有UserMapper
1.前面说bean 定义的过程时,可以知道,DefaultListableBeanFactory 容器中的 beanDefinitionMap 属性,保存了的bean 定义信息
FactoryBean对象创建
工厂对象实际上在之前就已经创建了,只不过是存在 AbstractAutowireCapableBeanFactory 类的 factoryBeanInstanceCache属性中
创建 MapperFactoryBean 这个工厂对象时,使用的是有参数构造器,参数就是我们的业务Mapper接口
1.根据beanName(userMapper) 获取 BeanDefiniton 对象,由于是工厂对象,所以执行getBean() 逻辑
1.MapperFactoryBean#checkDaoConfig() 在父类的初始化方法中进行调用的
2.在创建MapperFactoryBean 对象时,使用的是有参数构造器(反射),参数就是我们的UserMapper 接口
key = UserMapper.class
value = MapperProxyFactory
1.将Mapper接口保存到 MapperRegistry 对象的 knownMappers 属性中
2. 创建 MapperAnnotationBuilder 对象,解析接口以及接口的注解标注的方法,将每一个方法的信息(就类似xml的标签)封装到 MappedStatement对象中
3.不管是哪个声明sql的形式,都是在注册Mapper接口时去解析sql信息的
3.注册Mapper
2.执行工厂对象的初始化方法
2.熟悉spring的知道,有了bean定义信息后,才可以创建对象,那么这里来创建UserMapper 对象
UserMapper
但是容器中保存的value 明明是 MapperFactoryBean 对象,为什么注入的时候变成了 MapperProxy 呢?
为什么呢?
1.首先注入进来的是 MapperProxy 对象
1.前面说了注入 MapperFactoryBean 这个工厂对象时,实际注入的是 MapperFactoryBean#getObject() 方法,实际注入的是 MapperProxy 对象
1. 通过 SqlSessionFactory 对象打开一个 DefaultSqlSession 对象
后面的步骤就很清晰了,可以参考我画的mybatis 流程图
2.然后由 DefaultSqlSession 执行CRUD
来到 SqlSessionInterceptor 的 invoke() 方法中
由 里面的 SqlSessionTemplate 代理对象 sqlSessionProxy 进行调用
进而由 SqlSessionTemplate 对象调用
进而由 MapperMethod 对象调用
2.调用时将会来到 MapperProxy#invoke() 方法
调用UserMapper
2.那么当调用UserMapper#selectById 时如何工作?
HelloController
2.请求是如何工作的?
7.如何工作
spring-mybatis 整合过程
0 条评论
回复 删除
下一页