mybatis-spring 启动过程和调用过程
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 的初始化
- 创建 sqlSessionFactoryBean,这是一个被Spring管理的工厂bean
- 创建 sqlSessionFactory,这是一个 Spring 管理的 Bean,属于 mybatis 范畴
- 创建 sqlSessionTemplate,其中使用到了 sqlSessionFactory,属于 mybatis-spring 范畴
- 创建 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首先要被扫描,然后挨个生成代理类,等待调用。下面我们来看看这个过程:
- 使用 MapperScannerConfigurer
- scan() 扫描mapper
- setBeanClass
- 得到 MapperFactoryBean
- MapperFactoryBean.getObject() 方法
- configuration.getMapper
- getMapper
- MapperRegistry
- knownMappers
- 最终得到一组 MapperProxy,他们是原始 mapper 的代理。MapperProxy 实现了 InvocationHandler 接口,其中有 invoke 方法,在实际调用的时候执行。
说明:对于第4点,MapperFactoryBean 是一个工厂bean,在spring容器里,工厂bean是有特殊用途的,当spring将工厂bean注入到其他bean里时,它不是注入工厂bean本身,而是调用bean的getObject方法。
mybatis-spring 调用过程
sqlSessionFactory的初始化完成后,mapper的扫描和代理类被创建出来后,有了这两个前提条件,我们就可以来最终捋一捋 mybatis-spring 的调用过程了。
业务代码中,需要查询数据库,于是调用 mapper 中的一个方法
- MapperProxy invoke
- 2.1 if 判断
- 2.2 else if 判断
- 2.3 cachedMapperMethod 重点方法
- 2.4 mapperMethod.execute 重点方法,实际执行的方法
- 根据sql语句的类型,分情况处理
- case INSERT
- case UPDATE
- case DELETE
- case SELECT
- case FLUSH
以 SELECT 情况举例,将会执行 sqlSession.selectList。此时取出的,就是mapper的一个代理类
sqlSessionTemplate.sqlSessionProxy.selectList
- 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 启动过程和调用过程的更多相关文章
- 解析spring启动加载dubbo过程
一:简单配置 web.xml <context-param> <param-name>contextConfigLocation</param-name> < ...
- Spring源码分析之`BeanFactoryPostProcessor`调用过程
前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 本文内容: AbstractApplicationContext#refresh前部分的一点小内容 ...
- spring启动component-scan类扫描加载过程(转)
文章转自 http://www.it165.net/pro/html/201406/15205.html 有朋友最近问到了 spring 加载类的过程,尤其是基于 annotation 注解的加载过程 ...
- WebService—CXF整合Spring实现接口发布和调用过程
一.CXF整合Spring实现接口发布 发布过程如下: 1.引入jar包(基于maven管理) <!-- cxf --> <dependency> <groupId> ...
- Spring Mvc和Mybatis的多数据库访问配置过程
Spring Mvc 加Mybatis的多数据库访问源配置访问过程如下: 在applicationContext.xml进行配置 <?xml version="1.0" en ...
- mybatis源码分析(方法调用过程)
十一月月底,宿舍楼失火啦,搞得20多天没有网,目测直到放假也不会来了... 正题 嗯~,其实阅读源码不是为了应付面试,更重要的让你知道,大师是怎样去写代码的,同样是用Java,为啥Clinton Be ...
- Spring学习笔记(七)模拟实际开发过程的调用过程XML版-Setter方式注入
模拟实际开发过程的调用过程XML版-Setter方式注入 源码获取github [TOC] 1.项目结构 2.jar包跟上个一样 3.重写set方法 UserServiceImpl.java 1234 ...
- Spring IoC容器的初始化过程
Spring IoC容器的初始化包括 BeanDefinition的Resource定位.载入和注册 这三个基本的过程.IoC容器的初始化过程不包含Bean依赖注入的实现.Bean依赖的注入一般会发生 ...
- Spring之IOC容器初始化过程
Ioc容器的初始化是由refresh()方法来启动的,这个方法标志着Ioc容器的正式启动. 具体来说这个启动过程包括三个基本过程: 1.BeanDifinition的Resource定位 2.Bean ...
随机推荐
- Python IAQ中文版 - Python中少有人回答的问题
Python中少有人回答的问题 The Python IAQ: Infrequently Answered Questions 1 Q: 什么是"少有人回答的问题(Infrequently ...
- Kubernetes 系列(一):本地k8s集群搭建
我们需要做以下工作: (1)安装VMware,运行CentOs系统,一个做master,一个做node. (2)安装K8s. (3)安装docker和部分镜像会需要访问外网,所以你需要做些网络方面的准 ...
- mysql root用户登录后无法查看数据库全部表
可能是把root@localhost用户删掉了. 首先停掉mysql服务,在/etc/my.cnf中添加 skip-grant-tables,同时可以添加skip-networking选项来禁用网络功 ...
- redis常用笔记(第一版)
1.SINTER 说明:多key之间取交集数据 key1 = {a,b,c,d} key2 = {c} key3 = {a,c,e} SINTER key1 key2 key3 = {c} 2.sad ...
- 最近太多人问Protobuf的问题了,把这个重新搬出来!
pb杀手 我先让pbkiller做个自我介绍 pbkiller:我是一位专业的争对 protobuf 问题训练有素的杀手,我可以为您轻松搞定 protobuf 在 Cocos Creaotr 开发中的 ...
- WKWebView针对于Cordova的IOS平台性能提升
使用cordova做跨平台开发已久,针对于Android的性能与页面渲染问题仍然让人头疼,因为仍然有一部分人使用性能一般的手机,版本在 4.2-4.4之间,甚至都无法支持HTML5的flex布局,使得 ...
- 经典面试题golang实现方式(一)
以下所有题目的关键信息都会用[]括起来,我们不对题目进行分析,只给出题目的解决方案:如有疑问请不吝赐教. 题目: 请实现一个算法,确定一个字符串的所有字符[是否全都不同].这里我们要求[不允许使用额外 ...
- 从输入URL到页面渲染完成 -戈多编程
1.输入URL地址 2.浏览器根据域名查询IP地址 3.浏览器发送HTTP请求到web服务器 4.服务器返回一个永久重定向响应 5.浏览器会跟踪重定向地址 6.服务器处理请求 7.服务器返回一个HTM ...
- ubuntu14.04 安装tensorflow始末
基于ubuntu14.04 干净的系统一步步遇到的坑记录下来: 怀着平静学习的心情,问题总的能解决的! 1. 首先看了下当前python版本 python --version Python 2.7.6 ...
- Kubernetes集群的部署方式及详细步骤
一.部署环境架构以及方式 第一种部署方式 1.针对于master节点 将API Server.etcd.controller-manager.scheduler各组件进行yum install.编译安 ...