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中的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
接口代理的实现之前已经了解过了,这里不赘述。
以上是关于mybatis源码阅读mybatis与spring整合原理的主要内容,如果未能解决你的问题,请参考以下文章
从源码角度剖析 Spring 如何管理 mybatis 事务的