在日常使用中,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中的SqlSessionFactory、SqlSession、MapperProxy等对象都会用到,mybatis与spring进行整合只是做了一些包装工作。下面我们现有SqlSessionFactoryBean进行分析。
创建会话工厂
在mybatis与spring进行整合之后,创建会话工厂是由SqlSessionFactoryBean来实现的。该类的结构关系如下:
SqlSessionFactoryBean实现了FactoryBean和InitializingBean接口,在属性设置完成后会调用afterPropertiesSet()做一些额外操作。而在获取会话工厂实例时会调用FactoryBean的getObjext(),作为实例Bean返回。
afterPropertiesSet()很简单,直接调用buildSqlSessionFactory()来构建一个sqlSessionFactory对象。
getObjext()方法中直接先调用了afterPropertiesSet()方法,然后返回了sqlSessionFactory对象!!!因此,最关键的就是buildSqlSessionFactory()。
该方法主要做了以下事情:
- 判断
Configuration对象是否已经存在,也就是是否已经解析过。如果已经有对象,就覆盖一下属性。 - 如果
Configuration不存在,但是配置了configLocation属性,就根据mybatis-config.xml的文件路径,构建一个xmlConfigBuilder对象。然后通过xmlConfigBuilder获取一个Configuration对象。如果Configuration对象不存在,并且configLocation路径也没有,只能创建Configuration对象,并以默认值进行赋值。 - 后面就是基于当前
factory对象里面已有的属性,对targetConfiguration对象里面属性的赋值。比如别名,类型处理器,插件等属性。 - 如果没有明确指定事务工厂,默认使用
SpringManagedTransactionFactory。它创建的SpringManagedTransaction也有getConnection()和close()方法。 - 最后调用
sqlSessionFactoryBuilder.build()返回了一个DefaultSqlSessionFactory。
创建 SqlSession
在Spring里面,我们不是直接使用DefaultSqlSession的,而是对它进行了一个封装,这个SqlSession的实现类就是SqlSessionTemplate。
为什么不用
DefaultSqlSession?
因为DefaultSqlSession是线程不安全的,而SqlSessionTemplate是线程安全的。
我们继续看一下SqlSessionTemplate的实现:
可以看到,SqlSessionTemplate下的操作委派给了sqlSessionProxy对象处理。sqlSessionProxy的具体实现如下:
该代理对象的执行器就是SqlSessionInterceptor对象。下面看看该对象的invoke()方法执行了什么逻辑。
可以看到,invoke()方法中,首先调用了静态方法getSqlSession()获取sqlSession对象,然后再调用了sqlSession对象的方法。
sqlSession是会话级别的示例,也就意味不同会话中,调用getSqlSession()方法返回的实例是不一样的,而同一个会话中返回的sqlSession实例是同一个。
接口扫描注册
在实际使用中,通常都是在Service中直接通过@Autowired注入一个Mapper接口。这说明在spring启动过程中,肯定扫描了Mapper接口并将其注册到spring的容器中的。
在applicationContext.xml里面配置了一个MapperScannerConfigurer。MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口, BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的子类,可以通过编码的方式修改、新增或者删除某些Bean定义。
postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
由于实现了BeanDefinitionRegistryPostProcessor接口,因此spring会调用postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法。
整个方法的逻辑逻辑将Mapper接口扫描并注册Bean定义注册中心中。在这里的一个关键点是设置bean定义的beanClass为MapperFactoryBean。
如果某个bean的Class设置为一个FactoryBean类型,那么在实例化这个Bean的时候首先会实例化FactoryBean对象,然后调用FactoryBean对象的getObject()方法返回实际的对象!
这也就意味着,Mapper接口的实际对象是通过MapperFactoryBean的getObject()方法产生的。
接口注入使用
上面已经说过,Mapper接口的实际对象是通过MapperFactoryBean的getObject()方法产生的。并且MapperFactoryBean继承了SqlSessionDaoSupport,可以直接获取到sqlSessionTemplate对象。
getObject()方法的实现非常简单,直接通过SqlSession获取Mapper接口的代理对象。Mapper接口代理的实现之前已经了解过了,这里不赘述。
原创不易,觉得文章写得不错的小伙伴,点个赞👍 鼓励一下吧~
欢迎关注我的开源项目:一款适用于SpringBoot的轻量级HTTP调用框架