本应在开始读MyBatis源码时首先应该了解下MyBatis的SqlSession的四大对象:Executor、StatemenHandler、ParameterHandler、ResultHandler,但我想把这四大对象放到咱们源码中一步一步来解读。html
开始。sql
对MyBatis的使用咱们在最开始都已经知道能够经过xml配置文件的方式,也能够经过Java代码建立Configuration对象的方式。这二者其实是同样,xml配置文件的方式最终也是经过解析xml配置文件建立一个Configuration对象。可能对于不少人来讲MyBatis一般是和Spring配合使用,用了N年MyBatis也不能把MyBatis说个因此出来。写MyBatis的这个系列,正式但愿不要只光会用,还要懂其原理,熟悉一个语言、一个框架的特性原理才能在不一样场合使用不一样的特性。apache
回到正题,咱们说到使用MyBatis第一步就是配置,或者说第一个重要的对象就是Configuration。但我想要阅读的第一个源码并非Configuration类,咱们暂且知道它会贯穿整个MyBatis的生命周期,它是存放一些配置所在的地方便可。咱们要说的是MyBatis第二个重要部分——SqlSession的执行过程。session
从官方文档的说明,咱们能够知道SqlSession是由SqlSessionFactory建立,SqlSessionFactoryBuilder建立。框架
咱们经过代码简单回顾一下SQLSession实例的建立过程。ide
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession();
在建立一个SqlSession实例时,首先须要建立一个SqlSessionFactory实例,而又须要经过SqlSessionFactoryBuilder()的build静态方法来建立SqlSessionFactory。(关于这三者的做用域(Scope)及生命周期以前有介绍过,这里再也不多讲,参考《SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession做用域(Scope)和生命周期》)函数
因此咱们首先来看看SqlSessionFactoryBuilder这个类。它放置在package org.apache.ibatis.session包中。工具
1 public class SqlSessionFactoryBuilder { 2 3 public SqlSessionFactory build(Reader reader) { 4 return build(reader, null, null); 5 } 6 7 public SqlSessionFactory build(Reader reader, String environment) { 8 return build(reader, environment, null); 9 } 10 11 public SqlSessionFactory build(Reader reader, Properties properties) { 12 return build(reader, null, properties); 13 } 14 15 public SqlSessionFactory build(Reader reader, String environment, Properties properties) { 16 try { 17 XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); 18 return build(parser.parse()); 19 } catch (Exception e) { 20 throw ExceptionFactory.wrapException("Error building SqlSession.", e); 21 } finally { 22 ErrorContext.instance().reset(); 23 try { 24 reader.close(); 25 } catch (IOException e) { 26 // Intentionally ignore. Prefer previous error. 27 } 28 } 29 } 30 31 public SqlSessionFactory build(InputStream inputStream) { 32 return build(inputStream, null, null); 33 } 34 35 public SqlSessionFactory build(InputStream inputStream, String environment) { 36 return build(inputStream, environment, null); 37 } 38 39 public SqlSessionFactory build(InputStream inputStream, Properties properties) { 40 return build(inputStream, null, properties); 41 } 42 43 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { 44 try { 45 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); 46 return build(parser.parse()); 47 } catch (Exception e) { 48 throw ExceptionFactory.wrapException("Error building SqlSession.", e); 49 } finally { 50 ErrorContext.instance().reset(); 51 try { 52 inputStream.close(); 53 } catch (IOException e) { 54 // Intentionally ignore. Prefer previous error. 55 } 56 } 57 } 58 59 public SqlSessionFactory build(Configuration config) { 60 return new DefaultSqlSessionFactory(config); 61 } 62 63 }
咱们能够看到这个类用不少的构造方法,但主要分为三大类:一、第3-29行是经过读取字符流(Reader)的方式构件SqlSessionFactory。二、第31-57行是经过字节流(InputStream)的方式构件SqlSessionFacotry。三、第59行直接经过Configuration对象构建SqlSessionFactory。第一、2种方式是经过配置文件方式,第3种是经过Java代码方式。fetch
让咱们再仔细来看究竟是怎么构建出SqlSessionFactory的呢?以经过InputStream字节流的方式来看,和它相关的一共有4个构造方法,其中最后一个public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties),第2个和第3个参数并不陌生,这至关于在告诉这两个配置项environment、properties是能够经过在构建SqlSessionFactory的时候进行配置的或从新配置(此时优先级最高)。首先经过第45-46行代码XMLConfigBuilder工具类对配置文件进行解析成Configuration对象,再调用public SqlSessionFactory build(Configuration config)构建出SqlSessionFactory,因此兜兜转转,不论是配置文件仍是Java代码,最后都会通过解析经过Configuration对象产生SqlSessionFactory。ui
咱们能够发现第60行代码返回的是DefaultSqlSessionFactory实例,而不是SqlSessionFactory。那是由于实际上SqlSessionFactory是一个接口,而DefaultSqlSessionFactory是它的实现类。以下图所示。
在这里咱们暂且无论SqlSessionManager,咱们只需知道SqlSessionFactory有DefaultSqlSessionFactory和SqlSessionManager。在SqlSessionFactory能够猜想一下有什么方法。
回顾SqlSession的建立过程,其实咱们也能猜想获得SqlSessionFactory必定主要是建立SqlSession实例的方法。
1 public interface SqlSessionFactory { 2 3 SqlSession openSession(); 4 5 SqlSession openSession(boolean autoCommit); 6 SqlSession openSession(Connection connection); 7 SqlSession openSession(TransactionIsolationLevel level); 8 9 SqlSession openSession(ExecutorType execType); 10 SqlSession openSession(ExecutorType execType, boolean autoCommit); 11 SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level); 12 SqlSession openSession(ExecutorType execType, Connection connection); 13 14 Configuration getConfiguration(); 15 16 }
这么多的openSession重载方法,都是经过传入不一样的参数构造SqlSession实例,有经过设置事务是否自动提交"autoCommit",有设置执行器类型"ExecutorType"来构造的,还有事务的隔离级别等等。最后一个方法就告诉咱们能够经过SqlSessionFactory来获取Configuration对象。至于DefaultSqlSessionFactory对SqlSessionFactory的具体实现,除了以上方法以外,还包括了:openSessionFromDataSource、openSessionFromConnection、getTransactionFactoryFromEnvironment、closeTransaction。到这里咱们彷佛仍是只停留在表面,并无涉及相对比较底层的代码啊,别急。咱们这是刚走了一遍“SqlSession建立过程”的流程。下面咱们从SqlSessionFactoryBuilder第60行return new DefaultSqlSessionFactory(config)开始。
因为SqlSessionFactory的实现类DefaultSqlSessionFactory,源码过长,咱们在其中以截取关键的代码做为解读。
DefaultSqlSessionFactory中的第1行代码实际上就很是值得咱们思考:final关键字。
private final Configuration configuration;
为何会使用final关键字对Configuration对象进行修饰呢?Configuration应该是存在于MyBatis的整个生命周期那么意味着它应该是有且仅有一个实例的,而final关键字修饰的变量字段就表明它是不可变对象(《“不可变的对象”与“不可变的对象引用”》),这也刚好能解释说明官方User Guide中所说的SqlSessionFactory应该是单例的。但这是设计在前?仍是规则在前呢?若是是设计在前,那为何这样设计?若是是规则在前,是什么样的规则规定了这样作呢?我认为是设计在前。
首先,MyBatis认为配置文件之因此是配置文件那么就觉得着它只有一种配置(这个说法并非很全面,由于咱们已经见到了那么多的构造方法就说明在一个应用程序中能够经过不一样的场景配置选用不一样的配置,事实也如此),就比如咱们将一个新手机买回来事后,设置时间、日期就再也不去更改,但咱们可能会出国,这个时候就要配置选用另外一个时区的时间,不过我仍是使用的是这个手机的设置,换句话说,你的手机不可能有两个系统设置吧。因此Configuration对象实际上就是咱们手机上的系统设置。而SqlSessionFactory是经过Configuration来构造SqlSession的,对Configuration的引用固然是不可变的,若是可变,那至关于你手机里岂不是能够新建一个系统设置?那不就乱套了?索性final,对象不可变。此时也就建议SqlSessionFactory是单例的了,你构建N个SqlSessionFactory,它们也是经过一个Configuration对象来构造的SqlSession实例,那还有必要有N个SqlSessionFactory了吗?显然没有必要,因此最好就是将SqlSessionFactory设计为单例。一样可参考《SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession做用域(Scope)和生命周期》。
这才对DefaultSqlSessionFactory类第一句话进行了解读,接着就是实现SqlSessionFactory接口的8个构造方法。DefaultSqlSessionFactory并无直接实现这8个构造方法而是调用另外两个新的方法,这8个构造方法实际上分为两大类:一个是从数据源中获取SqlSession,一个是从Connection中获取SqlSession(包含Connection参数的那两个构造函数)。
先看从数据源中获取SqlSession。
1 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { 2 Transaction tx = null; 3 try { 4 final Environment environment = configuration.getEnvironment(); 5 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); 6 tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); 7 final Executor executor = configuration.newExecutor(tx, execType); 8 return new DefaultSqlSession(configuration, executor, autoCommit); 9 } catch (Exception e) { 10 closeTransaction(tx); // may have fetched a connection so lets call close() 11 throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); 12 } finally { 13 ErrorContext.instance().reset(); 14 } 15 }
若是没有传入ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit这三个参数就表明使用咱们Configuration对象中的配置(看来Executor、TransactionIsolationLevel、autoCommit是能够灵活配置的)。第8行建立出一个DefaultSqlSession实例,能够猜想SqlSession是一个接口而DefaultSqlSession是其实现类。对于SqlSession的建立过程,咱们立刻就要走到最后一步SqlSession的构建。而这也是最关键最重要最发杂的一步。