摘要: 本文结合《Spring源码深度解析》来分析Spring 5.0.6版本的源代码。若有描述错误之处,欢迎指正。

目录

一、SqlSessionFactoryBean的初始化

二、获取 SqlSessionFactoryBean 实例

通过Spring整合MyBatis的示例,我们感受到了Spring为用户更加快捷地进行开发所做的努力,开发人员的工作效率由此得到了显著的提升。但是,相对于使用来说,我们更想知道其背后所隐藏的秘密,Spring整合MyBatis是如何实现的呢?通过分析整合示例中的配置文件,我们可以知道配置的bean其实是成树状结构的,而在树的最顶层是类型为org.mybatis.spring.SqlSessionFactoryBean的bean,它将其他相关bean组装在了一起,那么,我们的分析就从此类开始。

通过配置文件我们分析,对于配置文件的读取解析,Spring应该通过org.mybatis.spring.SqlSessionFactoryBean封装了MyBatis中的实现。我们进入这个类,首先査看这个类的层次结构,如下图所示。

根据这个类的层次结构找出我们感兴趣的两个接口,FactoryBean和InitializingBean:

  • InitializingBean:实现此接口的bean会在初始化时调用其afterPropertiesSet方法来进行bean的逻辑初始化。
  • FactoryBean:一旦某个bean实现此接口,那么通过getBean方法获取bean时其实是获取此类的getObject()返回的实例。

我们首先以InitializingBean接口的afterPropertiesSet()方法作为突破点。

一、SqlSessionFactoryBean的初始化

査看org.mybatis.spring.SqlSessionFactoryBean类型的bean在初始化时做了哪些逻辑实现。

@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"); this.sqlSessionFactory = buildSqlSessionFactory();
}

很显然,此函数主要目的就是对于sqlSessionFactory的初始化,通过之前展示的独立使用MyBatis的示例,我们了解到SqlSessionFactory是所有MyBatis功能的基础。

protected SqlSessionFactory buildSqlSessionFactory() throws IOException {

    Configuration 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) {
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();
} else {
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);
LOGGER.debug(() -> "Scanned package: '" + packageToScan + "' for aliases");
}
} if (!isEmpty(this.typeAliases)) {
for (Class<?> typeAlias : this.typeAliases) {
configuration.getTypeAliasRegistry().registerAlias(typeAlias);
LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'");
}
} if (!isEmpty(this.plugins)) {
for (Interceptor plugin : this.plugins) {
configuration.addInterceptor(plugin);
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);
LOGGER.debug(() -> "Scanned package: '" + packageToScan + "' for type handlers");
}
} if (!isEmpty(this.typeHandlers)) {
for (TypeHandler<?> typeHandler : this.typeHandlers) {
configuration.getTypeHandlerRegistry().register(typeHandler);
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 {
xmlConfigBuilder.parse();
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) {
this.transactionFactory = new SpringManagedTransactionFactory();
} configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource)); if (!isEmpty(this.mapperLocations)) {
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
} try {
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();
}
LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
}
} else {
LOGGER.debug(() -> "Property 'mapperLocations' was not specified or no matching resources found");
} return this.sqlSessionFactoryBuilder.build(configuration);
}

从函数中可以看到,尽管我们还是习惯于将MyBatis的配置与Spring的配置独立开来,但是,这并不代表Spring中的配置不支持直接配置。也就是说,在上面提供的示例中,你完全可以取消配置中的configLocation,而把其中的属性直接写在SqlSessionFactoryBean中。

从这个函数中可以得知,配置文件还可以支持其他多种属性的配置,如configLocation、objectFactory、objectWrapperFactory、typeAliasesPackage、typeAliases、typeHandlersPackage、plugins、typeHandlers、transactionFactory、databaseIdProvider、mapperLocations。

其实,如果只按照常用的配置,那么我们只需要在函数最开始按照如下方式处理configuration:

xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();

根据configLocation构造XMLConfigBuilder并进行解析,但是,为了体现Spring更强大的兼容性,Spring还整合了MyBatis中其他属性的注入,并通过实例configuration来承载每一步所获取的信息并最终使用sqlSessionFactoryBuilder实例根据解析到的configuration创建SqlSessionFactory实例。

二、获取 SqlSessionFactoryBean 实例

由于SqlSessionFactoryBean实现了FactoryBean接口,所以当通过getBean方法获取对应实例时,其实是获取该类的getObject()函数返回的实例,也就是获取初始化后的sqlSessionFactory属性。

@Override
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
} return this.sqlSessionFactory;
}

Spring整合MyBatis(三)sqlSessionFactory创建的更多相关文章

  1. spring与mybatis三种整合方法

    spring与mybatis三种整合方法 本文主要介绍Spring与Mybatis三种常用整合方法,需要的整合架包是mybatis-spring.jar,可通过链接 http://code.googl ...

  2. Spring学习总结(六)——Spring整合MyBatis完整示例

    为了梳理前面学习的内容<Spring整合MyBatis(Maven+MySQL)一>与<Spring整合MyBatis(Maven+MySQL)二>,做一个完整的示例完成一个简 ...

  3. Spring学习总结(五)——Spring整合MyBatis(Maven+MySQL)二

    接着上一篇博客<Spring整合MyBatis(Maven+MySQL)一>继续. Spring的开放性和扩张性在J2EE应用领域得到了充分的证明,与其他优秀框架无缝的集成是Spring最 ...

  4. spring整合mybatis(hibernate)配置

    一.Spring整合配置Mybatis spring整合mybatis可以不需要mybatis-config.xml配置文件,直接通过spring配置文件一步到位.一般需要具备如下几个基本配置. 1. ...

  5. spring 整合 mybatis 中数据源的几种配置方式

    因为spring 整合mybatis的过程中, 有好几种整合方式,尤其是数据源那块,经常看到不一样的配置方式,总感觉有点乱,所以今天有空总结下. 一.采用org.mybatis.spring.mapp ...

  6. Mybatis学习(六)————— Spring整合mybatis

    一.Spring整合mybatis思路 非常简单,这里先回顾一下mybatis最基础的根基, mybatis,有两个配置文件 全局配置文件SqlMapConfig.xml(配置数据源,全局变量,加载映 ...

  7. Mybatis(六) Spring整合mybatis

    心莫浮躁~踏踏实实走,一步一个脚印,就算不学习,玩,能干嘛呢?人生就是那样,要找点有意思,打发时间的事情来做,而钻研技术,动脑动手的过程,还是比其他工作更有意思些~ so,努力啥的都是强迫自己做自以为 ...

  8. 简单探讨spring整合mybatis时sqlSession不需要释放关闭的问题

    https://blog.csdn.net/RicardoDing/article/details/79899686 近期,在使用spring和mybatis框架编写代码时,sqlSession不需要 ...

  9. Spring整合MyBatis小结

    MyBatis在Spring中的配置 我们在Spring中写项目需要运用到数据库时,现在一般用的是MyBatis的框架来帮助我们书写代码,但是学习了SSM就要知道M指的就是MyBatis,在此,在Sp ...

随机推荐

  1. yii page title, CMenu 中文不显示

    Page title: <?php echo CHtml::encode(iconv('gbk','utf-8',$this->pageTitle)); ?> CMenu: fram ...

  2. Debian Gun/linux基本用法

    添加软件源:vim /etc/apt/sources.list 在文本中添加如下内容:deb http://mirrors.163.com/debian/ stretch main non-free ...

  3. spring作用、spring注解、管理对象的作用域与生命周期、自动装配、Spring的框架包有哪些作用是什么

    Spring 1. 作用 创建和管理对象,使得开发过程中,可以不必使用new关键字创建对象,而是直接获取对象!并且,还可以通过一些配置,使得某些获取到的对象,其中某些属性已经是被赋值的! 2. Spr ...

  4. 前端独立引用 ejs模版

    ejs 用法不再多说,网自行查阅.一个是基于nodeJS平台运行的EJS,另外一个是在浏览器执行的EJS.这里要说的是html 独立引入ejs.min.js 使用的一个注意点. 如:index.htm ...

  5. MySQL的索引与优化

    写在前面:索引对查询的速度有着至关重要的影响,理解索引也是进行数据库性能调优的起点.考虑如下情况,假设数据库中一个表有10^6条记录,DBMS的页面大小为4K,并存储100条记录.如果没有索引,查询将 ...

  6. 前端 css+js实现返回顶部功能

    描述: 本文主要是讲,通过css+js实现网页中的[返回顶部]功能. 实现代码: HTML: <div> <button onclick="returnTop()" ...

  7. ecloipse背景修改豆沙

    Eclipse背景色的修改 Eclipse背景色的修改,修改为豆沙色  值是85 123 205 一.修改编辑区   ①这个比较简单一般都会不多说. 1.首先点击Window 然后选择Preferen ...

  8. Material适配2 - 高级篇

    版权声明: 欢迎转载,但请保留文章原始出处 作者:GavinCT 出处:http://www.cnblogs.com/ct2011/p/4493439.html 继续Material系列,先从Tool ...

  9. 从Azure上构建Linux应用程序映像

    下图描述了总体的虚拟机的VHD映像生成以及发布到 Azure Azure 镜像市场的全过程: 具体步骤如下: 从Azure管理平台上Linux申请虚拟机, 安装和配置您要发布的应用软件产品,制作成映像 ...

  10. Linux Transparent Huge Pages 对 Oracle 的影响

    1 Transparent Huge Pages 说明 官网上有2篇文章对THP 做了说明: https://access.redhat.com/solutions/46111 https://acc ...