mybatis源码阅读mybatis与spring整合原理

Posted Java_宇宁

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mybatis源码阅读mybatis与spring整合原理相关的知识,希望对你有一定的参考价值。

在日常使用中,mybatis都是和spring整合使用的。本文将会从源码角度介绍mybatis与spring整合原理。

整合配置

在正式介绍之前,我们先来回忆一下将mybatis和spring框架进行整合需要进行哪些配置。为了看起来更加直观,我们还是以传统的xml配置来说明。

在Spring的applicationContext.xml里面配置SqlSessionFactoryBean,这个Bean是用来帮助我们创建会话的,其中还要指定全局配置文件mapper映射器文件的路径。

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="configLocation" value="classpath:mybatis-config.xml"></property>
  <property name="mapperLocation" value="classpath:mapper/*.xml"></property>
  <property name="dataSource" ref="dataSource"></property>
</bean>

然后在 applicationContext.xml 配置需要扫描 Mapper 接口的路径。

<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  <property name="basePackage" value="com.github.chentianming11.mybatis" />
</bean>

Spring对MyBatis的对象进行了管理,但是并不会替换MyBatis的核心对象。因此在Mybatis中的SqlSessionFactorySqlSessionMapperProxy等对象都会用到,mybatis与spring进行整合只是做了一些包装工作。下面我们现有SqlSessionFactoryBean进行分析。

创建会话工厂

在mybatis与spring进行整合之后,创建会话工厂是由SqlSessionFactoryBean来实现的。该类的结构关系如下: image.png

SqlSessionFactoryBean实现了FactoryBeanInitializingBean接口,在属性设置完成后会调用afterPropertiesSet()做一些额外操作。而在获取会话工厂实例时会调用FactoryBeangetObjext(),作为实例Bean返回。

image.png

afterPropertiesSet()很简单,直接调用buildSqlSessionFactory()来构建一个sqlSessionFactory对象。

image.png

getObjext()方法中直接先调用了afterPropertiesSet()方法,然后返回了sqlSessionFactory对象!!!因此,最关键的就是buildSqlSessionFactory()image.png

该方法主要做了以下事情:

  1. 判断Configuration对象是否已经存在,也就是是否已经解析过。如果已经有对象,就覆盖一下属性。
  2. 如果Configuration不存在,但是配置了configLocation属性,就根据mybatis-config.xml的文件路径,构建一个xmlConfigBuilder对象。然后通过xmlConfigBuilder获取一个Configuration对象。如果Configuration对象不存在,并且configLocation路径也没有,只能创建Configuration对象,并以默认值进行赋值。
  3. 后面就是基于当前factory对象里面已有的属性,对targetConfiguration对象里面属性的赋值。比如别名,类型处理器,插件等属性。
  4. 如果没有明确指定事务工厂,默认使用SpringManagedTransactionFactory。它创建的SpringManagedTransaction也有getConnection()close()方法。
  5. 最后调用sqlSessionFactoryBuilder.build()返回了一个DefaultSqlSessionFactory

创建 SqlSession

Spring里面,我们不是直接使用DefaultSqlSession的,而是对它进行了一个封装,这个SqlSession的实现类就是SqlSessionTemplate

为什么不用DefaultSqlSession?

因为DefaultSqlSession是线程不安全的,而SqlSessionTemplate是线程安全的。

我们继续看一下SqlSessionTemplate的实现: image.png

可以看到,SqlSessionTemplate下的操作委派给了sqlSessionProxy对象处理。sqlSessionProxy的具体实现如下:

image.png

该代理对象的执行器就是SqlSessionInterceptor对象。下面看看该对象的invoke()方法执行了什么逻辑。

image.png

可以看到,invoke()方法中,首先调用了静态方法getSqlSession()获取sqlSession对象,然后再调用了sqlSession对象的方法。 sqlSession是会话级别的示例,也就意味不同会话中,调用getSqlSession()方法返回的实例是不一样的,而同一个会话中返回的sqlSession实例是同一个。

接口扫描注册

在实际使用中,通常都是在Service中直接通过@Autowired注入一个Mapper接口。这说明在spring启动过程中,肯定扫描了Mapper接口并将其注册到spring的容器中的。

applicationContext.xml里面配置了一个MapperScannerConfigurerMapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口, BeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor的子类,可以通过编码的方式修改、新增或者删除某些Bean定义。

image.png

postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)

由于实现了BeanDefinitionRegistryPostProcessor接口,因此spring会调用postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法。

image.pngimage.pngimage.png

整个方法的逻辑逻辑将Mapper接口扫描并注册Bean定义注册中心中。在这里的一个关键点是设置bean定义的beanClassMapperFactoryBean

如果某个bean的Class设置为一个FactoryBean类型,那么在实例化这个Bean的时候首先会实例化FactoryBean对象,然后调用FactoryBean对象的getObject()方法返回实际的对象!

这也就意味着,Mapper接口的实际对象是通过MapperFactoryBeangetObject()方法产生的。

接口注入使用

上面已经说过,Mapper接口的实际对象是通过MapperFactoryBeangetObject()方法产生的。并且MapperFactoryBean继承了SqlSessionDaoSupport,可以直接获取到sqlSessionTemplate对象。

image.png

getObject()方法的实现非常简单,直接通过SqlSession获取Mapper接口的代理对象。Mapper接口代理的实现之前已经了解过了,这里不赘述。


 

以上是关于mybatis源码阅读mybatis与spring整合原理的主要内容,如果未能解决你的问题,请参考以下文章

从源码角度剖析 Spring 如何管理 mybatis 事务的

MapperScannerConfigurer源码解析

精尽MyBatis源码分析 - SQL执行过程之 StatementHandler

mybatis源码阅读(动态代理)

MyBatis 源码篇-MyBatis-Spring 剖析

mybatis源码分析 mybatis与spring事务管理分析