SqlSessionTemplate是如何保证的MyBatis中的SqlSession的线程安全的?
一,DefaultSqlSession的线程不安全性
在MyBatis的架构中的SqlSession是提供给外层调用的顶层接口,实现类有:DefaultSqlSession,SqlSessionManager以及MyBatis的弹簧提供的实现SqlSessionTemplate默认的实现类为DefaultSqlSession如类图结构如下所示:。
对于MyBatis的提供的原生实现类来说,用的最多的就是DefaultSqlSession,但我们知道DefaultSqlSession这个类不是线程安全的如下!
二,SqlSessionTemplate是如何使用DefaultSqlSession的
而在我们开发的时候肯定会用到春天,也会用到MyBatis的弹簧框架,在使用的MyBatis与弹簧集成的时候我们会用到了SqlSessionTemplate这个类,例如下边的配置,注入一个单例的SqlSessionTemplate对象:
- 1
- 2
- 3
SqlSessionTemplate的源代码注释如下:
通过源码我们何以看到SqlSessionTemplate实现了SqlSession接口,也就是说我们可以使用SqlSessionTemplate来代理以前的DefaultSqlSession完成对数据库的操作,但是是DefaultSqlSession这个类不是线程安全的,所以DefaultSqlSession这个类不可以被设置成单例模式的。
如果是常规开发模式的话,我们每次在使用DefaultSqlSession的时候都从SqlSessionFactory对象当中获取一个就可以了但是与弹簧集成以后,弹簧提供了一个全局唯一的SqlSessionTemplate对象来完成DefaultSqlSession的功能,问题就是:无论是多个道使用一个SqlSessionTemplate,还是一个道使用一个SqlSessionTemplate,SqlSessionTemplate都是对应一个SQLSESSION对象,当多个网络线程调用同一个岛时,它们使用的是同一个SqlSessionTemplate,也就是同一个SqlSession的,那么它?是如何确保线程安全的呢让我们一起来分析一下:
三,SqlSessionTemplate是如何保证DefaultSqlSession线程安全的
(1)首先,通过如下代码创建代理类,表示创建的SqlSessionFactory的代理类的实例,该代理类实现的SqlSession接口,定义了方法拦截器,如果调用代理类实例中实现的SqlSession接口定义的方法,该调用则被导向SqlSessionInterceptor的invoke方法(代理对象的InvocationHandler就是SqlSessionInterceptor,如果把它命名为SqlSessionInvocationHandler则更好理解!)核心
代码就在SqlSessionInterceptor的invoke方法当中。
- 1
- 2
- 3
- 4
- 五
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 三十
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
在上面的调用方法当中使用了两个工具方法分别是:
- 1
- 2
那么这两个方法又是如何与春天的事物进行关联的呢?
1,getSqlSession方法如下:
- 1
- 2
- 3
- 4
- 五
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 三十
2,closeSqlSession方法如下:
- 1
- 2
- 3
- 4
- 五
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
大致的分析到此为止,可能有些许不够顺畅,不过:纸上得来终觉浅,绝知此事要躬行还希望小伙伴打开自己的编译器,找到此处的代码,认真走一遍流程!
其实通过上面的代码我们可以看出的MyBatis在很多地方都用到了代理模式,代理模式可以说是一种经典模式,其实不紧紧在这个地方用到了代理模式,春天的事物,AOP,MyBatis的数据库连接池技术,MyBatis的的核心原理(如何在只有接口没有实现类的情况下完成数据库的操作!)等技术都使用了代理技术。
四,SqlSessionManager又是什么鬼?
上述说了一个SqlSession的实现还有一个SqlSessionManager,那么SqlSessionManager到底是什么个东西哪且看定义如下:?
?你可能会发现SqlSessionManager的构造方法竟然是私人的,那我们怎么创建这个对象哪其实SqlSessionManager创建对象是通过的newInstance的方法创建对象的,但需要注意的是他虽然有私有的构造方法,并且提供给我们了一个公有的的newInstance方法,但它并不是一个单例模式!
newInstance有很多重载的方法,如下所示:
SqlSessionManager的openSession方法及其重载的方法是直接通过调用其中的底层封装的SqlSessionFactory对象的openSession方法来创建SqlSession对象的,重载方法如下:
SqlSessionManager中实现了SqlSession的接口中的方法,例如:选择,更新等,都是直接调用sqlSessionProxy代理对象中相应的方法在创建该代理对像的时候使用的InvocationHandler的对象是SqlSessionInterceptor,他是定义在SqlSessionManager的一个内部类,其定义如下:
五,总结
综上所述,我们应该大致了解了DefaultSqlSession和SqlSessionManager之间的区别:
1,DefaultSqlSession的内部没有提供像SqlSessionManager一样通过ThreadLocal的的方式来保证线程的安全性;
2,SqlSessionManager是通过localSqlSession这个ThreadLocal的变量,记录与当前线程绑定的SqlSession的对象,供当前线程循环使用,从而避免在同一个线程多次创建的SqlSession对象造成的性能损耗;
3,DefaultSqlSession不是线程安全的,我们在进行原生开发的时候,需要每次为一个操作都创建一个SqlSession的对象,其性能可想而知;
六,扩展面试题
那么问题来了:
1,为什么MyBatis的弹簧框架中不直接使用线程安全的SqlSessionManager(SqlSessionFactory中它是线程安全的)而是使用DefaultSqlSession这个线程不安全的类,并通过动态代理的方式来保证DefaultSqlSession操作的线程安全性哪?
2,DefaultSqlSession中是如何通过执行器来表现策略模式的或者DefaultSqlSession如何使用策略模式模式的?