前言
在前面幾篇文章中我們主要分析了Mybatis的單獨(dú)使用,在實(shí)際在常規(guī)項(xiàng)目開發(fā)中,大部分都會(huì)使用mybatis與Spring結(jié)合起來使用,畢竟現(xiàn)在不用Spring開發(fā)的項(xiàng)目實(shí)在太少了。本篇文章便來介紹下Mybatis如何與Spring結(jié)合起來使用,并介紹下其源碼是如何實(shí)現(xiàn)的。
Mybatis-Spring使用
添加maven依賴
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.1.5</version>
</dependency>
Mybatis和Spring整合方式即mybatis-spring
在src/main/resources下添加mybatis-config.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<typeAlias alias="User" type="com.yibo.bean.User" />
</typeAliases>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="helperDialect" value="mysql"/>
</plugin>
</plugins>
</configuration>
在src/main/resources/mapper路徑下添加User.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yibo.mapper.UserMapper">
<select id="getUser" parameterType="int" resultType="com.yibo.bean.User">
SELECT * FROM USER WHERE id = #{id}
</select>
</mapper>
在src/main/resources/路徑下添加beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/test"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath:mapper/*.xml" />
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.chenhao.mapper" />
</bean>
</beans>
注解的方式
以上分析都是在spring的XML配置文件applicationContext.xml進(jìn)行配置的,mybatis-spring也提供了基于注解的方式來配置sqlSessionFactory和Mapper接口。
sqlSessionFactory主要是在@Configuration注解的配置類中使用@Bean注解的名為sqlSessionFactory的方法來配置。
Mapper接口主要是通過在@Configuration注解的配置類中結(jié)合@MapperScan注解來指定需要掃描獲取mapper接口的包。
@Configuration
@MapperScan("com.yibo.mapper")
public class AppConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.addScript("schema.sql")
.build();
}
@Bean
public DataSourceTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
//創(chuàng)建SqlSessionFactoryBean對象
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
//設(shè)置數(shù)據(jù)源
sessionFactory.setDataSource(dataSource());
//設(shè)置Mapper.xml路徑
sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
// 設(shè)置MyBatis分頁插件
PageInterceptor pageInterceptor = new PageInterceptor();
Properties properties = new Properties();
properties.setProperty("helperDialect", "mysql");
pageInterceptor.setProperties(properties);
sessionFactory.setPlugins(new Interceptor[]{pageInterceptor});
return sessionFactory.getObject();
}
}
對照Spring-Mybatis的方式,也就是對照beans.xml文件來看
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath:mapper/*.xml" />
</bean>
就對應(yīng)著SqlSessionFactory的生成,類似于原生Mybatis使用時(shí)的以下代碼
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build( Resources.getResourceAsStream("mybatis-config.xml"));
而UserMapper代理對象的獲取,是通過掃描的形式獲取,也就是MapperScannerConfigurer這個(gè)類
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.yibo.mapper" />
</bean>
對應(yīng)著Mapper接口的獲取,類似于原生Mybatis使用時(shí)的以下代碼:
qlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
Mybatis和SpringBoot整合方式
即引入mybatis-spring-boot-starter和mapper-spring-boot-starter
- application.properties中的配置
mybatis.type-aliases-package=com.yibo.source.code.domain.entity
mybatis.mapper-locations=classpath:mapper/*.xml
mapper.identity=MYSQL
mapper.not-empty=false
- 主配置類中加入@MapperScan
@MapperScan("com.yibo.source.code.mapper")//掃描Mapper接口
這樣我們就可以在Service中直接從Spring的BeanFactory中獲取了,如下
public class UserServiceImpl implements UserService{
@Autowired
private UserMapper userMapper;
}
所以我們現(xiàn)在就主要分析下在Spring中是如何生成SqlSessionFactory和Mapper接口的
SqlSessionFactoryBean的設(shè)計(jì)與實(shí)現(xiàn)
大體思路:
mybatis-spring為了實(shí)現(xiàn)spring對mybatis的整合,即將mybatis的相關(guān)組件作為spring的IOC容器的bean來管理,使用了spring的FactoryBean接口來對mybatis的相關(guān)組件進(jìn)行包裝。spring的IOC容器在啟動(dòng)加載時(shí),如果發(fā)現(xiàn)某個(gè)bean實(shí)現(xiàn)了FactoryBean接口,則會(huì)調(diào)用該bean的getObject方法,獲取實(shí)際的bean對象注冊到IOC容器,其中FactoryBean接口提供了getObject方法的聲明,從而統(tǒng)一spring的IOC容器的行為。
SqlSessionFactory作為mybatis的啟動(dòng)組件,在mybatis-spring中提供了SqlSessionFactoryBean來進(jìn)行包裝,所以在spring項(xiàng)目中整合mybatis,首先需要在spring的配置,如XML配置文件applicationContext.xml中,配置SqlSessionFactoryBean來引入SqlSessionFactory,即在spring項(xiàng)目啟動(dòng)時(shí)能加載并創(chuàng)建SqlSessionFactory對象,然后注冊到spring的IOC容器中,從而可以直接在應(yīng)用代碼中注入使用或者作為屬性,注入到mybatis的其他組件對應(yīng)的bean對象。在applicationContext.xml的配置如下:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
// 數(shù)據(jù)源
<property name="dataSource" ref="dataSource" />
// mapper.xml的資源文件,也就是SQL文件
<property name="mapperLocations" value="classpath:mybatis/mapper/**/*.xml" />
//mybatis配置mybatisConfig.xml的資源文件
<property name="configLocation" value="classpath:mybatis/mybitas-config.xml" />
</bean>
接口設(shè)計(jì)與實(shí)現(xiàn)
SqlSessionFactory的接口設(shè)計(jì)如下:實(shí)現(xiàn)了spring提供的FactoryBean,InitializingBean和ApplicationListener這三個(gè)接口,在內(nèi)部封裝了mybatis的相關(guān)組件作為內(nèi)部屬性,如mybatisConfig.xml配置資源文件引用,mapper.xml配置資源文件引用,以及SqlSessionFactoryBuilder構(gòu)造器和SqlSessionFactory引用。
// 解析mybatisConfig.xml文件和mapper.xml,設(shè)置數(shù)據(jù)源和所使用的事務(wù)管理機(jī)制,將這些封裝到Configuration對象
// 使用Configuration對象作為構(gòu)造參數(shù),創(chuàng)建SqlSessionFactory對象,其中SqlSessionFactory為單例bean,最后將SqlSessionFactory單例對象注冊到spring容器。
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
private static final Log LOGGER = LogFactory.getLog(SqlSessionFactoryBean.class);
// mybatis配置mybatisConfig.xml的資源文件
private Resource configLocation;
//解析完mybatisConfig.xml后生成Configuration對象
private Configuration configuration;
// mapper.xml的資源文件
private Resource[] mapperLocations;
// 數(shù)據(jù)源
private DataSource dataSource;
// 事務(wù)管理,mybatis接入spring的一個(gè)重要原因也是可以直接使用spring提供的事務(wù)管理
private TransactionFactory transactionFactory;
private Properties configurationProperties;
// mybatis的SqlSessionFactoryBuidler和SqlSessionFactory
private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
private SqlSessionFactory sqlSessionFactory;
// 實(shí)現(xiàn)FactoryBean的getObject方法
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
this.afterPropertiesSet();
}
return this.sqlSessionFactory;
}
// 實(shí)現(xiàn)InitializingBean的
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.dataSource, "Property 'dataSource' is required");
Assert.notNull(this.sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
Assert.state(this.configuration == null && this.configLocation == null || this.configuration == null || this.configLocation == null, "Property 'configuration' and 'configLocation' can not specified with together");
this.sqlSessionFactory = this.buildSqlSessionFactory();
}
// 單例
public boolean isSingleton() {
return true;
}
}
我們重點(diǎn)關(guān)注FactoryBean,InitializingBean這兩個(gè)接口,spring的IOC容器在加載創(chuàng)建SqlSessionFactoryBean的bean對象實(shí)例時(shí),會(huì)調(diào)用InitializingBean的afterPropertiesSet方法進(jìn)行對該bean對象進(jìn)行相關(guān)初始化處理。
InitializingBean的afterPropertiesSet方法
大家最好看一下我前面關(guān)于Spring源碼的文章,有Bean的生命周期詳細(xì)源碼分析,我們現(xiàn)在簡單回顧一下,在getBean()時(shí)initializeBean方法中調(diào)用InitializingBean的afterPropertiesSet,而在前一步操作populateBean中,以及將該bean對象實(shí)例的屬性設(shè)值好了,InitializingBean的afterPropertiesSet進(jìn)行一些后置處理。此時(shí)我們要注意,populateBean方法已經(jīng)將SqlSessionFactoryBean對象的屬性進(jìn)行賦值了,也就是xml中property配置的dataSource,mapperLocations,configLocation這三個(gè)屬性已經(jīng)在SqlSessionFactoryBean對象的屬性進(jìn)行賦值了,后面調(diào)用afterPropertiesSet時(shí)直接可以使用這三個(gè)配置的值了。
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {
// bean對象實(shí)例創(chuàng)建的核心實(shí)現(xiàn)方法
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
// 1.新建Bean包裝類
BeanWrapper instanceWrapper = null;
//如果RootBeanDefinition是單例的,則移除未完成的FactoryBean實(shí)例的緩存
if (mbd.isSingleton()) {
// 2.如果是FactoryBean,則需要先移除未完成的FactoryBean實(shí)例的緩存
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 3.根據(jù)beanName、mbd、args,使用對應(yīng)的策略創(chuàng)建Bean實(shí)例,并返回包裝類BeanWrapper
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 4.拿到創(chuàng)建好的Bean實(shí)例
final Object bean = instanceWrapper.getWrappedInstance();
// 5.拿到Bean實(shí)例的類型
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
// 6.應(yīng)用后置處理器MergedBeanDefinitionPostProcessor,允許修改MergedBeanDefinition,
// Autowired注解、Value注解正是通過此方法實(shí)現(xiàn)注入類型的預(yù)解析
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
// 7.判斷是否需要提早曝光實(shí)例:單例 && 允許循環(huán)依賴 && 當(dāng)前bean正在創(chuàng)建中
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 8.提前曝光beanName的ObjectFactory,用于解決循環(huán)引用
// 8.1 應(yīng)用后置處理器SmartInstantiationAwareBeanPostProcessor,允許返回指定bean的早期引用,若沒有則直接返回bean
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
// 初始化bean實(shí)例。
Object exposedObject = bean;
try {
// InstantiationAwareBeanPostProcessor執(zhí)行:
// (1). 調(diào)用InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation,
// (2). 調(diào)用InstantiationAwareBeanPostProcessor的postProcessProperties和postProcessPropertyValues
// 9.對bean進(jìn)行屬性填充;其中,可能存在依賴于其他bean的屬性,則會(huì)遞歸初始化依賴的bean實(shí)例
populateBean(beanName, mbd, instanceWrapper);
// Aware接口的方法調(diào)用
// BeanPostProcess執(zhí)行:調(diào)用BeanPostProcessor的postProcessBeforeInitialization
// 調(diào)用init-method:首先InitializingBean的afterPropertiesSet,然后應(yīng)用配置的init-method
// BeanPostProcess執(zhí)行:調(diào)用BeanPostProcessor的postProcessAfterInitialization
// 10.對bean進(jìn)行初始化
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
if (earlySingletonExposure) {
// 11.如果允許提前曝光實(shí)例,則進(jìn)行循環(huán)依賴檢查
Object earlySingletonReference = getSingleton(beanName, false);
// 11.1 earlySingletonReference只有在當(dāng)前解析的bean存在循環(huán)依賴的情況下才會(huì)不為空
if (earlySingletonReference != null) {
if (exposedObject == bean) {
// 11.2 如果exposedObject沒有在initializeBean方法中被增強(qiáng),則不影響之前的循環(huán)引用
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
// 11.3 如果exposedObject在initializeBean方法中被增強(qiáng) && 不允許在循環(huán)引用的情況下使用注入原始bean實(shí)例
// && 當(dāng)前bean有被其他bean依賴
// 11.4 拿到依賴當(dāng)前bean的所有bean的beanName數(shù)組
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
// 11.5 嘗試移除這些bean的實(shí)例,因?yàn)檫@些bean依賴的bean已經(jīng)被增強(qiáng)了,他們依賴的bean相當(dāng)于臟數(shù)據(jù)
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
// 11.6 移除失敗的添加到 actualDependentBeans
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
// 11.7 如果存在移除失敗的,則拋出異常,因?yàn)榇嬖赽ean依賴了“臟數(shù)據(jù)”
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
// Register bean as disposable.
try {
// 12.注冊用于銷毀的bean,執(zhí)行銷毀操作的有三種:自定義destroy方法、DisposableBean接口、DestructionAwareBeanPostProcessor
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
// 13.完成創(chuàng)建并返回
return exposedObject;
}
}
如上,在populateBean階段,dataSource,mapperLocations,configLocation這三個(gè)屬性已經(jīng)在SqlSessionFactoryBean對象的屬性進(jìn)行賦值了,調(diào)用afterPropertiesSet時(shí)直接可以使用這三個(gè)配置的值了。那我們來接著看看afterPropertiesSet方法
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
@Override
public void afterPropertiesSet() throws Exception {
notNull(dataSource, "Property 'dataSource' is required");
notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
"Property 'configuration' and 'configLocation' can not specified with together");
// 創(chuàng)建sqlSessionFactory
this.sqlSessionFactory = buildSqlSessionFactory();
}
}
SqlSessionFactoryBean的afterPropertiesSet方法實(shí)現(xiàn)如下:調(diào)用buildSqlSessionFactory方法創(chuàng)建用于注冊到spring的IOC容器的sqlSessionFactory對象。我們接著來看看buildSqlSessionFactory
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
// 配置類
Configuration configuration;
// 解析mybatis-Config.xml文件,
// 將相關(guān)配置信息保存到configuration
XMLConfigBuilder xmlConfigBuilder = null;
if (this.configuration != null) {
configuration = this.configuration;
if (configuration.getVariables() == null) {
configuration.setVariables(this.configurationProperties);
} else if (this.configurationProperties != null) {
configuration.getVariables().putAll(this.configurationProperties);
}
//資源文件不為空
} else if (this.configLocation != null) {
//根據(jù)configLocation創(chuàng)建xmlConfigBuilder,XMLConfigBuilder構(gòu)造器中會(huì)創(chuàng)建Configuration對象
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
//將XMLConfigBuilder構(gòu)造器中創(chuàng)建的Configuration對象直接賦值給configuration屬性
configuration = xmlConfigBuilder.getConfiguration();
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
}
configuration = new Configuration();
if (this.configurationProperties != null) {
configuration.setVariables(this.configurationProperties);
}
}
if (this.objectFactory != null) {
configuration.setObjectFactory(this.objectFactory);
}
if (this.objectWrapperFactory != null) {
configuration.setObjectWrapperFactory(this.objectWrapperFactory);
}
if (this.vfs != null) {
configuration.setVfsImpl(this.vfs);
}
if (hasLength(this.typeAliasesPackage)) {
String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeAliasPackageArray) {
configuration.getTypeAliasRegistry().registerAliases(packageToScan,
typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
}
}
}
if (!isEmpty(this.typeAliases)) {
for (Class<?> typeAlias : this.typeAliases) {
configuration.getTypeAliasRegistry().registerAlias(typeAlias);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered type alias: '" + typeAlias + "'");
}
}
}
if (!isEmpty(this.plugins)) {
for (Interceptor plugin : this.plugins) {
configuration.addInterceptor(plugin);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered plugin: '" + plugin + "'");
}
}
}
if (hasLength(this.typeHandlersPackage)) {
String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeHandlersPackageArray) {
configuration.getTypeHandlerRegistry().register(packageToScan);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");
}
}
}
if (!isEmpty(this.typeHandlers)) {
for (TypeHandler<?> typeHandler : this.typeHandlers) {
configuration.getTypeHandlerRegistry().register(typeHandler);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered type handler: '" + typeHandler + "'");
}
}
}
if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls
try {
configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException e) {
throw new NestedIOException("Failed getting a databaseId", e);
}
}
if (this.cache != null) {
configuration.addCache(this.cache);
}
if (xmlConfigBuilder != null) {
try {
//解析mybatis-Config.xml文件,并將相關(guān)配置信息保存到configuration
xmlConfigBuilder.parse();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
}
} catch (Exception ex) {
throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
} finally {
ErrorContext.instance().reset();
}
}
if (this.transactionFactory == null) {
//事務(wù)默認(rèn)采用SpringManagedTransaction,這一塊非常重要,我將在后買你單獨(dú)寫一篇文章講解Mybatis和Spring事務(wù)的關(guān)系
this.transactionFactory = new SpringManagedTransactionFactory();
}
// 為sqlSessionFactory綁定事務(wù)管理器和數(shù)據(jù)源
// 這樣sqlSessionFactory在創(chuàng)建sqlSession的時(shí)候可以通過該事務(wù)管理器獲取jdbc連接,從而執(zhí)行SQL
configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));
// 解析mapper.xml
if (!isEmpty(this.mapperLocations)) {
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
}
try {
// 解析mapper.xml文件,并注冊到configuration對象的mapperRegistry
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
configuration, mapperLocation.toString(), configuration.getSqlFragments());
xmlMapperBuilder.parse();
} catch (Exception e) {
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
} finally {
ErrorContext.instance().reset();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
}
}
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
}
}
// 將Configuration對象實(shí)例作為參數(shù),
// 調(diào)用sqlSessionFactoryBuilder創(chuàng)建sqlSessionFactory對象實(shí)例
return this.sqlSessionFactoryBuilder.build(configuration);
}
}
buildSqlSessionFactory的核心邏輯:解析mybatis配置文件mybatisConfig.xml和mapper配置文件mapper.xml并封裝到Configuration對象中,最后調(diào)用mybatis的sqlSessionFactoryBuilder來創(chuàng)建SqlSessionFactory對象。這一點(diǎn)相當(dāng)于前面介紹的原生的mybatis的初始化過程。另外,當(dāng)配置中未指定事務(wù)時(shí),mybatis-spring默認(rèn)采用SpringManagedTransaction,這一點(diǎn)非常重要,請大家先在心里做好準(zhǔn)備。此時(shí)SqlSessionFactory已經(jīng)創(chuàng)建好了,并且賦值到了SqlSessionFactoryBean的sqlSessionFactory屬性中。
FactoryBean的getObject方法定義
FactoryBean:創(chuàng)建某個(gè)類的對象實(shí)例的工廠。
spring的IOC容器在啟動(dòng),創(chuàng)建好bean對象實(shí)例后,會(huì)檢查這個(gè)bean對象是否實(shí)現(xiàn)了FactoryBean接口,如果是,則調(diào)用該bean對象的getObject方法,在getObject方法中實(shí)現(xiàn)創(chuàng)建并返回實(shí)際需要的bean對象實(shí)例,然后將該實(shí)際需要的bean對象實(shí)例注冊到spring容器;如果不是則直接將該bean對象實(shí)例注冊到spring容器。
SqlSessionFactoryBean的getObject方法實(shí)現(xiàn)如下:由于spring在創(chuàng)建SqlSessionFactoryBean自身的bean對象時(shí),已經(jīng)調(diào)用了InitializingBean的afterPropertiesSet方法創(chuàng)建了sqlSessionFactory對象,故可以直接返回sqlSessionFactory對象給spring的IOC容器,從而完成sqlSessionFactory的bean對象的注冊,之后可以直接在應(yīng)用代碼注入或者spring在創(chuàng)建其他bean對象時(shí),依賴注入sqlSessionFactory對象。
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
@Override
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
}
// 直接返回sqlSessionFactory對象
// 單例對象,由所有mapper共享
return this.sqlSessionFactory;
}
}
總結(jié)
由以上分析可知,spring在加載創(chuàng)建SqlSessionFactoryBean的bean對象實(shí)例時(shí),調(diào)用SqlSessionFactoryBean的afterPropertiesSet方法完成了sqlSessionFactory對象實(shí)例的創(chuàng)建;在將SqlSessionFactoryBean對象實(shí)例注冊到spring的IOC容器時(shí),發(fā)現(xiàn)SqlSessionFactoryBean實(shí)現(xiàn)了FactoryBean接口,故不是SqlSessionFactoryBean對象實(shí)例自身需要注冊到spring的IOC容器,而是SqlSessionFactoryBean的getObject方法的返回值對應(yīng)的對象需要注冊到spring的IOC容器,而這個(gè)返回值就是SqlSessionFactory對象,故完成了將sqlSessionFactory對象實(shí)例注冊到spring的IOC容器。