Mybatis-Spring 学习笔记
先进到这个类里面看看
分别实现了这三个类FactoryBean, InitializingBean, ApplicationListener
因为实现了InitializingBean里的void afterPropertiesSet() throws Exception;方法, 所以会执行到这里
最终调用里面的buildSqlSessionFactory()方法,继续往下看:
首先是判断在配置文件里面有没有配置configuration(这里我没有配置)
直接执行到后面else
因为没有配置configuration所以直接new了一个,再设置属性
后面一大篇就是检测判断是否配置里面的一些配置(大部分都不用配)
下面就说些常用的
这个是判断是否配置插件的
是否设置缓存
下面这个就是检测mapperLocations, 最终是通过 xmlMapperBuilder.parse();这个方法来进行解析的进入到这个方法,发现这个方法是mybatis包里面的方法,通过configurationElement(parser.evalNode("/mapper"));这个方法来解析mapper配置文件
解析完成之后返回了一个sqlSessionFactory对象
再进到build()方法看一下,这里实际上返回的是DefaultSqlSessionFactory对象
所以这里的buildSqlSessionFactory()这个方法的实际作用就是:
1,解析了配置文件(dataSource mapperLocation)
2,将mapper.xml放到了MappedStatement这个集合中
3,返回了一个DefaultSqlSessionFactory对象
所以最开始的afterPropertiesSet()这个方法就是把得到的defaultSqlSessionFactory赋值给sqlSessionFactory
那么下面的getObject()方法就是获取这个sqlSessionFactory
默认单例的
再来看第二个类:
先分析这个类实现的接口
BeanDefinitionRegistryPostProcessor : 这个接口是Spring中的,就是一个beanDefinition的后置处理器,其实这个类就是在对象生成BeanDefinition还没有放入IOC容器的时候对这个类进行干预的接口,里面提供了一个回调的方法,通过这个方法就可以修改bean的定义
InitializingBean: 这个接口里面也只提供了一个方法,是在把对象放到IOC容器之后的回调方法, 对象创建完成之后进行值的设置
主要就是上面这两个方法; 先来分析第一个,把对象放到IOC容器前都干了些什么下面就是读取并配置我们的配置信息, 最后调用scan()方法,所以进入scan()方法
发现scan()方法里面调用了doScan()方法,并且把扫描的包路径传了进去
进入doScan()方法,注意,这里进入的是中间包里面的ClassPathMapperScanner这个类里面(Spring的包)的doScan()方法,扫面自己的文件,因为Spring里面自己定义了要扫描那些注解
进到这个方法里面首先调用的是父类里面的doScan方法获取到所有的BeanDefinition的定义(就是所有类的定义),这里扫描的是所有的mapper接口, 下面进行if判断当beanDefinitions不为空(意思是扫描到有mapper接口),就执行processBeanDefinitions()这个方法,在这个方法里面完成bean的定义
执行到这里首先咱们翻译下这个注释:意思就是说本来要放到IOC容器的bean是mapper的这个接口, 但是实际上放到IOC容器的是MapperFactoryBean这个类的对象,也就是说实际上放到容器的是mapper的工厂对象
所以首先就遍历上一个方法里面得到每个的类的描述对象,通过这句代码definition.setBeanClass(this.mapperFactoryBean.getClass());设置bean描述对象的class对象为bean的工厂对象
后面的代码就是将刚刚读取到的配置文件的信息设置到beanDefinition里面,
最后设置了自动注入的模型为通过类型注入
那么既然放到IOC容器的是MapperFactoryBean的对象,那么下面到这个类里面看看
实现了FactoryBean这个接口,FactoryBean这个接口里面有三个方法分别是:T getObject() throws Exception; 得到工厂创建的实例对象
Class<?> getObjectType(); 得到创建的这个实例的类型
boolean isSingleton(); 这个对象是否是单例的
这里就得出结论: 继承了SqlSessionDaoSupport这个方法目的是为了获取里面的getSqlSession()方法,实现FactoryBean的目的就是为了返回当前MapperFactoryBean中的一个实例
private Class mapperInterface; 这个属性就是mapper接口的全路径
MapperFactoryBean中的这个方法就是调用了父类中的getSqlSession()获取sqlSession对象来获取mapper对象
这里我们再来看看这个方法里面, 是通过sqlSession.getMapper来获取实例,
先看看这个getSqlSession()获取到的到底是什么再往下看
所以说这里得到的sqlSession对象是SqlSessionTemplate而不是DefaultSqlSession, 那么就有一个疑问,为什么这里不使用DefaultSqlSession呢?再进入SqlSessionTemplate(sqlSessionFactory);这个构造器一探究竟
最终调用了两个构造器进入到如下方法, 发现了什么…这个SqlSessionTemplate对象,实际上就是一个代理对象, 这里代理的就是defaultSqlSession这个对象 .回到刚刚那个问题为什么不直接使用defaultSqlSession这个对象呢,我们进到DefaultSqlSession看一看, 这里作者写的很清楚,这个类是线程不安全的, 从下面的成员变量也可以看出来这个类是线程不安全的, 因为放到IOC容器中的类默认是单例的, 那么单例的就一定存在线程安全问题, 所以这里使用SqlSessionTemplate来保证放到IOC容器中的对象是线程安全
再来看一下SqlSessionTemplate这个类, 从作者的注释和下面的属性都是静态常量,所以SqlSessionTemplate是线程安全的 ,
再回到上面,如下图所示,那么既然SqlSessionTemplate是线程安全的,为什么不直接返回这个类的对象, 而是使用动态代理返回DefaultSqlSession的代理对象呢
下面来看一下这个动态代理类在执行方法的时候都干了些什么,先进到new SqlSessionInterceptor()这个方法里面(最终动态代理执行的方法)
看看怎么获取这个SQLSession的
那么再看看这个持有器都干了什么,进到getResource(sessionFactory)这个方法
再进到doGetResource(actualKey)方法里所以来看看这个resources
这里使用了ThreadLocal来储存这个map, 而map中放的是defaultSqlSession的持有器, 这里使用ThreadLocal来唯一标记一个线程,用来保证defaultSqlSession的持有器的线程是安全的同时就保证了一个ThreadLocal里面只有唯一一个defaultSqlSession, (就相当于每个线程来的时候分配了一个属于自己的defaultSqlSession)
这里回到刚刚那个方法, 获取到持有器后通过持有器和executorType(执行器,这个执行器是最后用来执行SQL语句用的)获取到defaultSqlSession ,
前面说到过,实际上放到单例池的是mapperFactoryBean这个对象,那么我们为您看看这个工厂对象是怎么实例化mapper的,首先进到这个类,发现这俄格类是实现了FactoryBean的,来看看这个工厂怎么获取对象的
这里实际上就是调用了mybatis里面的getMapper()方法,而这个getMapper方法返回的就是mapper接口的代理对象,最终在myBatis的方法里面执行操作数据库。就成功的连接了mybatis和spring
仅个人学习笔记,如有不足,还有劳各路大佬多多指教~