Mybatis3源码分析(5)spring集成分析与mybatis所用到的设计模式
这里我们以传统的 Spring 为例,因为配置更直观,在 Spring 中使用配置类注解是一样的。在前面文章里面,我基于编程式的工程已经弄清楚了 MyBatis 的工作流程、核心模块和底层原理。编程式的工程,也就是 MyBatis 的原生 API 里面有三个核心对象: SqlSessionFactory、SqlSession、MapperProxy 大部分时候我们不会在项目中单独使用 MyBatis 的工程,而是集成到 Spring 里面使用,但是却没有看到这三个对象在代码里面的出现。我们直接注入了一个 Mapper 接口,调用它的方法。 所以有几个关键的问题,我要弄清楚:
- SqlSessionFactory 是什么时候创建的?
- SqlSession 去哪里了?为什么不用它来 getMapper?
- 为什么@Autowired 注入一个接口,在使用的时候却变成了代理对象?在 IOC的容器里面我们注入的是什么? 注入的时候发生了什么事情?
关键配置
引入mybatis-spring的整合jar包 然后在 Spring 的 applicationContext.xml 里面配置 SqlSessionFactoryBean,它是用来帮助我们创建会话的,其中还要指定全局配置文件和 mapper 映射器文件的路径
|
|
然后在 applicationContext.xml 配置需要扫描 Mapper 接口的路径。在 Mybatis 里面有几种方式,第一种是配置一个 MapperScannerConfigurer。
|
|
创建会话工厂
先记住实现了这几个接口
这个方法是spring IOC容器里面,对象设置属性完成后,可以调用的方法
这个是从ioc容器里面拿到这个对象的时候就会调用这个getobject方法另外两个是获取信息的。
很熟悉的配置文件解析
最后通过parse进入
现在已经是mybatis的操作了。在mybatis 的包里面了
也就是说spring 在创建SqlSessionFactoryBean的时候会通过getobject方法解析对应配置文件创建config设置属性
最后调用了build方法
这个和编程式mybatis单独使用调用的默认的DefaultSqlSessionFactory一样
获得会话
先看看DefaultSqlSessionFactory 我看到官方给的一个注释
|
|
实际上我们用DefaultSqlSessionFactory的时候他就是单例的,在多个service之间共享,那一定就会存在线程安全问题。 我们看看mybatis-spring包提供的实现类:进入这个类我就看见注释上就写着
|
|
为什么线程安全呢:我们随便找个方法对比
这个sqlsessionProxy一看就有鬼:这个jdk动态代理又来了
然后我们进入被代理类SqlSessionInterceptor看看
我一步步进入发现是存放在一个threadLocal里面也就是我们获取到的每个DefaultSqlSession都是一个单独的–>也就是一个事务一个sqlsession所以是线程安全的
我继续看他是怎么管理的–>这里就是代理的DefaultSqlSession的–>并且创建了一个事务管理器
我发现只是减少次数没有关闭,之前我们看到,是通过一个事务管理器创建的这个容器;也就是说在我们同一个事务里面可以使用相同的会话,然后每次都减少,直到只有一个的时候就关闭会话
我们知道在 Spring 里面会用 SqlSessionTemplate 替换 DefaultSqlSession,那么接下来看一下怎么在DAO层拿到一个SqlSessionTemplate。MyBatis 里面,它提供了一个 SqlSessionDaoSupport,里面持有一个SqlSessionTemplate 对象,并且提供了一个 getSqlSession()方法,让我们获得一个SqlSessionTemplate。
也就是说我们让 DAO 层的实现类继承 SqlSessionDaoSupport,就可以获得SqlSessionTemplate,然后在里面封装 SqlSessionTemplate 的方法。也就是说很多封装好的增删改的插件基本上就是继承这个SqlSessionDaoSupport早期的ibatis也就是这样来的。
{% raw %}
对象 | 生命周期 |
---|---|
SqlSessionTemplate | Spring 中 SqlSession 的替代品,是线程安全的,通过代理的方式调用 DefaultSqlSession 的方法 |
SqlSessionInterceptor(内部类) | 代理对象,用来代理 DefaultSqlSession,在 SqlSessionTemplate 中使用 |
SqlSessionDaoSupport | 用于获取 SqlSessionTemplate,只要继承它即可 |
MapperFactoryBean | 注册到 IOC 容器中替换接口类,继承了 SqlSessionDaoSupport 用来获取SqlSessionTemplate,因为注入接口的时候,就会调用它的 getObject()方法 |
SqlSessionHolder | 控制 SqlSession 和事务 |
接口扫描注册
在MapperScannerConfigurer一步步找到这个方法 在注册时并没有注册这个类本身的beanclass,而是注册了mapperFactoryBean 我们看看官方注释说明
|
|
那我们去看看mapperFactorBean–>果然继承了SqlSessionDaoSupport
所以:通过spring IOC 扫描注测接口本身的时候替换成了beanclass替换成了MapperFactoryBean这样也就可以拿到SqlSessionTemplate
接口 | 方法 | 作用 |
---|---|---|
FactoryBean | getObject() | 返回由 FactoryBean 创建的 Bean 实例 |
InitializingBean | afterPropertiesSet() | bean 属性初始化完成后添加操作 |
BeanDefinitionRegistryPostProcessor | postProcessBeanDefinitionRegistry() | 注入 BeanDefination 时添加操作 |
注入使用
在spring 注入初始化时mapper作为有注解的属性,这个时候会调用MapperFactoryBean–>然后调用getObject方法(因为他继承了SqlSessionDaoSupport所以能拿到SqlSessionTemplate)->然后就可以拿到我们的mapper代理对象(因为实现了 SqlSession)然后就可以调用mapper的数据库方法了
设计模式总结
{% raw %}
设计模式 | 类 |
---|---|
工厂 | SqlSessionFactory、ObjectFactory、MapperProxyFactory |
建造者 | XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuidler |
单例模式 | SqlSessionFactory、Configuration、ErrorContext |
代理模式 | 绑定:MapperProxy 延迟加载:ProxyFactory(CGLIB、JAVASSIT) 插件:Plugin Spring 集成 MyBaits:SqlSessionTemplate 的内部类 SqlSessionInterceptor MyBatis 自带连接池:PooledDataSource 管理的 PooledConnection 日志打印:ConnectionLogger、StatementLogger |
适配器模式 | logging 模块,对于 Log4j、JDK logging 这些没有直接实现 slf4j 接口的日志组件,需要适配器 |
模板方法 | BaseExecutor 与子类 SimpleExecutor、BatchExecutor、ReuseExecutor |
装饰器模式 | LoggingCache、LruCache 等对 PerpectualCache 的装饰 CachingExecutor 对其他 Executor 的装饰 |
责任链模式 | InterceptorChain |
策略模式 | RoutingStatementHandler |