Spring 整合Mybatis源码分析 学习笔记

    技术2022-07-11  124

    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

    仅个人学习笔记,如有不足,还有劳各路大佬多多指教~

    Processed: 0.008, SQL: 9