mybatis-spring 可以为我们做什么

mybatis框架已经很不错了,它把配置和执行sql的通用过程抽象出来。只要你符合mybatis框架的要求,首先有正确的配置,然后有model,interface层,sql语句,还有bean定义让interface和sql关联起来,那么当你执行interface中的方法的时候,mybatis框架就会为你找到对应的sql的可执行的statement,然后执行并返回结果。

可是这样还不够,最大的问题在于许多bean定义,必须一个一个手写,并且要保证interface和sql的名称和位置要填写正确。mybatis-spring最大的贡献在于它的bean扫描机制,只要注解使用正确,那么它可以为你自动扫描所有interface和sql语句,并且建立bean定义让它们关联起来。Spring还可以为动态的Bean定义创建缓存,这非常酷。

另外,既然融入了Spring框架,那么mybatis配置信息和SqlSessionFactory之类的信息,也可以用 Spring Bean 来管理。Spring 可以在它的层面上为它们做一些缓存。

mybatis执行sql的完整流程

我们回顾一下在单一的mybatis的机制中,配置的加载和sql的执行的完整流程。

// 解析配置文件,生成配置
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource); // 根据配置,构建一个SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 得到一个真正可用的SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession(); // 从SqlSession获取interface的代理
ArticleMapper articleMapperProxy = sqlSession.getMapper(ArticleMapper.class); // 执行代理类中的方法
Article article = articleMapperProxy.selectByPrimaryKey("123"); // 以下省略对 article 的操作

mybatis-spring 启动过程

我们先来说说mybatis-spring框架的启动过程。mybatis-spring 决定接管 sqlSessionFactory 和 sqlSession,并且为 sqlSession 创建代理类 sqlSessionProxy。除此之外,mybatis-spring 决定扫描所有interface层的mapper,然后接管所有 mapper 的代理类。2条线我们分开来看。

线1:sqlSessionFactory 和 sqlSession 的初始化

  1. 创建 sqlSessionFactoryBean,这是一个被Spring管理的工厂bean
  2. 创建 sqlSessionFactory,这是一个 Spring 管理的 Bean,属于 mybatis 范畴
  3. 创建 sqlSessionTemplate,其中使用到了 sqlSessionFactory,属于 mybatis-spring 范畴
  4. 创建 sqlSessionProxy

SqlSessionFactory是一个十分重要的工厂类,让我们来回顾一下SqlSessionFactory中有哪些信息:

# sqlSessionFactory 中的重要信息

sqlSessionFactory
configuration
environment # 里面有 dataSource 信息
mapperRegistry
config # 里面有配置信息
knownMappers # 里面有所有的 mapper
mappedStatements # 里面有所有 mapper 的所有方法
resultMaps # 里面有所有 xml 中的所有 resultMap
sqlFragments # 里面有所有的 sql 片段

这些信息非常重要,在不久的将来创建 sqlSessionProxy 和 将来创建 mapperProxy 的时候,都需要使用里面的信息。

在 mybatis-spring 框架中,sqlSessionFactory由Spring管理,让我们来看一下 sqlSessionFactory 是如何创建出来的。

 @Bean(name = "sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
if (StringUtils.hasText(this.properties.getConfig())) {
factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfig()));
} else {
if (this.interceptors != null && this.interceptors.length > 0) {
factory.setPlugins(this.interceptors);
}
factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
factory.setMapperLocations(this.properties.getMapperLocations());
}
return factory.getObject();
}

注意:SqlSessionFactoryBean 是一个工厂bean,它的作用就是解析mybatis 配置(数据源、别名等),然后通过 getObject方法返回一个 SqlSessionFactory 实例。我们先看下SqlSessionFactoryBean是在初始化的时候作了哪些工作。

让我们来看一下 SqlSessionFactoryBean 的源码:

 public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
private static final Log LOGGER = LogFactory.getLog(SqlSessionFactoryBean.class);
private Resource configLocation;
private Configuration configuration;
private Resource[] mapperLocations;
private DataSource dataSource;
private TransactionFactory transactionFactory;
private Properties configurationProperties;
private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
private SqlSessionFactory sqlSessionFactory;
private String environment = SqlSessionFactoryBean.class.getSimpleName();
private boolean failFast;
private Interceptor[] plugins;
private TypeHandler<?>[] typeHandlers;
private String typeHandlersPackage;
private Class<?>[] typeAliases;
private String typeAliasesPackage;
private Class<?> typeAliasesSuperType;
private DatabaseIdProvider databaseIdProvider;
private Class<? extends VFS> vfs;
private Cache cache;
private ObjectFactory objectFactory;
private ObjectWrapperFactory objectWrapperFactory; public SqlSessionFactoryBean() {
}
...
}

我们可以看到,这个类实现了FactoryBean、InitializingBean和ApplicationListener接口,对应的接口在bean初始化的时候又执行了一些特定的方法,此处不再展开。现在来看看都有哪些重要的方法会被执行,这些方法又做了哪些工作。

// FactoryBean中的方法
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
this.afterPropertiesSet();
} return this.sqlSessionFactory;
}

通过观察代码,getObject方法最终返回 sqlSessionFactory,如果 sqlSessionFactory 为空就会执行 afterPropertiesSet 中的 buildSqlSessionFactory 构建sqlSessionFactory,在构建sqlSessionFactory时mybatis会去解析配置文件,构建configuation。后面的onApplicationEvent主要是监听应用事件时做的一些事情(不展开,有兴趣的同学可以自己去了解下)。

我们来看看 afterPropertiesSet 方法是怎么将属性设置进去的:

// 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"); // 看到了我们熟悉的build方法
this.sqlSessionFactory = this.buildSqlSessionFactory();
}

buildSqlSessionFactory 的主要方法是:this.sqlSessionFactoryBuilder.build(configuration); 此处不再展开。到这里为止,sqlSessionFactory 已经创建完成,下面我们来简单看看 sqlSessionTemplate 的创建过程:

@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory, this.properties.getExecutorType());
}

如果跟踪进去,就会发现 new SqlSessionTemplate 的同时,会创建 sqlSessionProxy,此处不再展开。

线2:mapper 的扫描和代理类的创建

interface首先要被扫描,然后挨个生成代理类,等待调用。下面我们来看看这个过程:

  1. 使用 MapperScannerConfigurer
  2. scan() 扫描mapper
  3. setBeanClass
  4. 得到 MapperFactoryBean
  5. MapperFactoryBean.getObject() 方法
  6. configuration.getMapper
  7. getMapper
  8. MapperRegistry
  9. knownMappers
  10. 最终得到一组 MapperProxy,他们是原始 mapper 的代理。MapperProxy 实现了 InvocationHandler 接口,其中有 invoke 方法,在实际调用的时候执行。

说明:对于第4点,MapperFactoryBean 是一个工厂bean,在spring容器里,工厂bean是有特殊用途的,当spring将工厂bean注入到其他bean里时,它不是注入工厂bean本身,而是调用bean的getObject方法。

mybatis-spring 调用过程

sqlSessionFactory的初始化完成后,mapper的扫描和代理类被创建出来后,有了这两个前提条件,我们就可以来最终捋一捋 mybatis-spring 的调用过程了。

  1. 业务代码中,需要查询数据库,于是调用 mapper 中的一个方法

  2. MapperProxy invoke
    • 2.1 if 判断
    • 2.2 else if 判断
    • 2.3 cachedMapperMethod 重点方法
    • 2.4 mapperMethod.execute 重点方法,实际执行的方法
  3. 根据sql语句的类型,分情况处理
    • case INSERT
    • case UPDATE
    • case DELETE
    • case SELECT
    • case FLUSH
  4. 以 SELECT 情况举例,将会执行 sqlSession.selectList。此时取出的,就是mapper的一个代理类

  5. sqlSessionTemplate.sqlSessionProxy.selectList

  6. SqlSessionInterceptor invoke
    • 6.1 getSqlSession,用到了 sqlSessionFactory,使用了sqlSessionHolder 技术,有就拿一个,没有就新建一个。无论如何,都会有一个 sqlSession
    • 6.2 defaultSqlSession.selectList 重点,之后是 query -> queryFromDatabase -> doQuery -> 1. prepareStatement 2. execute
    • 6.3 closeSqlSession

我们可以发现,在 mybatis-spring 框架中,真正 sqlSession 的创建,是在调用interface中的方法的时候才进行的。更细节的过程可以参考上文。

参考资料

  • https://segmentfault.com/a/1190000015165470

创作时间:06/08/2019 21:00

mybatis-spring 启动过程和调用过程的更多相关文章

  1. 解析spring启动加载dubbo过程

    一:简单配置 web.xml <context-param> <param-name>contextConfigLocation</param-name> < ...

  2. Spring源码分析之`BeanFactoryPostProcessor`调用过程

    前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 本文内容: AbstractApplicationContext#refresh前部分的一点小内容 ...

  3. spring启动component-scan类扫描加载过程(转)

    文章转自 http://www.it165.net/pro/html/201406/15205.html 有朋友最近问到了 spring 加载类的过程,尤其是基于 annotation 注解的加载过程 ...

  4. WebService—CXF整合Spring实现接口发布和调用过程

    一.CXF整合Spring实现接口发布 发布过程如下: 1.引入jar包(基于maven管理) <!-- cxf --> <dependency> <groupId> ...

  5. Spring Mvc和Mybatis的多数据库访问配置过程

    Spring Mvc 加Mybatis的多数据库访问源配置访问过程如下: 在applicationContext.xml进行配置 <?xml version="1.0" en ...

  6. mybatis源码分析(方法调用过程)

    十一月月底,宿舍楼失火啦,搞得20多天没有网,目测直到放假也不会来了... 正题 嗯~,其实阅读源码不是为了应付面试,更重要的让你知道,大师是怎样去写代码的,同样是用Java,为啥Clinton Be ...

  7. Spring学习笔记(七)模拟实际开发过程的调用过程XML版-Setter方式注入

    模拟实际开发过程的调用过程XML版-Setter方式注入 源码获取github [TOC] 1.项目结构 2.jar包跟上个一样 3.重写set方法 UserServiceImpl.java 1234 ...

  8. Spring IoC容器的初始化过程

    Spring IoC容器的初始化包括 BeanDefinition的Resource定位.载入和注册 这三个基本的过程.IoC容器的初始化过程不包含Bean依赖注入的实现.Bean依赖的注入一般会发生 ...

  9. Spring之IOC容器初始化过程

    Ioc容器的初始化是由refresh()方法来启动的,这个方法标志着Ioc容器的正式启动. 具体来说这个启动过程包括三个基本过程: 1.BeanDifinition的Resource定位 2.Bean ...

随机推荐

  1. 【IT技术概念】WebAPI与传统的WebService有哪些不同?

    在.net平台下,有大量的技术让你创建一个HTTP服务,像Web Service,WCF,现在又出了Web API.在.net平台下,你有很多的选择来构建一个HTTP Services.我分享一下我对 ...

  2. 机器学习回顾篇(6):KNN算法

    1 引言 本文将从算法原理出发,展开介绍KNN算法,并结合机器学习中常用的Iris数据集通过代码实例演示KNN算法用法和实现. 2 算法原理 KNN(kNN,k-NearestNeighbor)算法, ...

  3. Activiti(1) - TaskRuntime API 入门

    目录 TaskRuntime API pom.xml 注册TaskRuntime实例 角色与分组 任务事件监听器 DemoApplication 源码 Activiti 是一个自动化工作流框架.它能帮 ...

  4. ELK系列(一):安装(elasticsearch + logstash + kibana)

    因为公司使用ELK的缘故,这两天尝试在阿里云上安装了下ELK,这里做个笔记,有兴趣的同学可以看下. 先大致介绍下ELK,ELK是三个组件的缩写,分别是elasticsearch.logstash.ki ...

  5. ELK 学习笔记之 elasticsearch启动时Warning解决办法

    elasticsearch启动时Warning解决办法: 转载:http://www.dajiangtai.com/community/18136.do?origin=csdn-geek&dt ...

  6. Kafka 学习笔记之 删除Topic

    删除Topic 1. 显示所有Topic信息,testTopic是我们将要删除的Topic 2. 首先确认server.properties下面配置是否已经加上delete.topic.enable= ...

  7. B/S 端构建的基于 WebGL 3D 可视化档案馆管理系统

    前言 档案管理系统是通过建立统一的标准以规范整个文件管理,包括规范各业务系统的文件管理的完整的档案资源信息共享服务平台,主要实现档案流水化采集功能.为企事业单位的档案现代化管理,提供完整的解决方案,档 ...

  8. SpringBootSecurity学习(20)前后端分离版之OAuth2.0刷新token

    刷新token 前面的例子和配置都是从头开始申请授权码和令牌,现在来看一下如何根据获取令牌时,回参中的 refresh_token 来刷新令牌.现在在项目中配置的是内存模式的默认用户名密码,第一步先改 ...

  9. iOS性能优化-异步绘制

    参考地址:https://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/ 很久以前就看过这篇文章,但是也只是看过就过了,没有去整 ...

  10. Mysql存储引擎以及锁机制

    一.常用命令 1.查看引擎(默认为InnoDB) 查看mysql提供的存储引擎:show engienes 查看mysql当前默认的存储引擎:show variables like '%storage ...