你猜一下哪一个先执行?反正不要按常规来。html
1 <plugins> 2 <plugin interceptor="com.Interceptor1"></plugin> 3 <plugin interceptor="com.Interceptor2"></plugin> 4 </plugins>
以前看有的博客分析源码,都没提到这一点。以前我只是用一下而已,这个顺序测试一下其实结论也很容易得到,可是我有一种看源码的屎命感。MyBatis还算人性化提供了拦截器,iBatis里面就没有了,不过也能够实现。这里要探究拦截器的源码就不得不提到MyBatis的源码,也就是执行流程了。这要是摊开说就有点大了,为了写好这篇,我决定今天晚上回去不打dota了,贡献真实够大的了。java
MyBatis的做用
名义上来讲MyBatis是一个半ORM框架,用了一个半字是由于MyBatis并无彻底起到一个ORM框架的做用(好比Hibernate),还有一半工做是须要咱们参与进来——编写SQL语句。MyBatis替咱们干的活是啥?帮咱们把参数和配置化SQL语句映射成数据库中真正执行的SQL,而后把结果帮咱们封装好,并返回回来。好处很容已说明,配置灵活,加强开发人员对SQL语句的控制,减小了冗余的对象封装工做。sql
官方的说法以下:数据库
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎全部的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。apache
MyBatis的架构
沿着上面说到的,咱们接下来能够看一下MyBatis用了什么架构来完成上面的工做。要注意,talk is cheap,实际上的话还要牵涉不少工做(好比Session,事务等)。在网上看了一些描述MyBatis架构的图,看来一夜不打dota是不可能了呀。session
功能上的架构:mybatis
源码中的结构:架构
架构看上去并不复杂,按三层来分的。以前看的不多,若是你是和我同样的小白话能够一块儿来看下,从头看起吧,接口和配置文件先开始。app
最简单的方式开始MyBatis
这里最简单的意思是,咱们先抛开Spring,只在一个简单的Maven项目中使用MyBatis,看看它是如何运行的。先起一个简单的Maven项目并加上MyBatis的依赖。框架
已经作好了可是,额,写起来估计能够新开一篇了。
单独使用MyBatis代码
其实核心代码只有两个,第一个是SessionUtils用于提供Session,第二个是使用Session进行CRUD操做的代码
1 public class SessionUtils { 2 private static SqlSessionFactory sessionFactory; 3 static { 4 try { 5 // 使用MyBatis提供的Resources类加载mybatis的配置文件 6 Reader reader = Resources.getResourceAsReader("mybatis-config.xml"); 7 // 构建sqlSession的工厂 8 sessionFactory = new SqlSessionFactoryBuilder().build(reader); 9 } catch (Exception e) { 10 e.printStackTrace(); 11 } 12 } 13 14 /** 15 * 获取SqlSession 16 * @return SqlSession 17 */ 18 public static SqlSession getSession() { 19 return sessionFactory.openSession(); 20 } 21 }
1 @Test 2 public void testInsert() { 3 SqlSession session = null; 4 try { 5 session = SessionUtils.getSession(); 6 StudentMapper studentMapper = session.getMapper(StudentMapper.class); 7 Long affectedLines = studentMapper.insert(build()); 8 System.out.println("affectedLines = " + (affectedLines == null ? 0 : affectedLines)); 9 session.commit(); 10 } catch (Exception e) { 11 e.printStackTrace(); 12 if(session != null) { 13 session.rollback(); 14 } 15 } finally { 16 if(session != null) { 17 session.close(); 18 } 19 } 20 }
SqlSessionFactory的生成
观察这两段代码引出了两个核心的类:SqlSessionFactory和Session。
SqlSessionFactoryBuilder:单纯的就是为了建立SqlSessionFactory,功能很单一。
1 // 这是一个普类, 而不是接口, 这里把方法都隐去了 2 public class SqlSessionFactoryBuilder { 3 public SqlSessionFactory build(Reader reader); 4 public SqlSessionFactory build(Reader reader, String environment); 5 public SqlSessionFactory build(Reader reader, Properties properties); 6 public SqlSessionFactory build(Reader reader, String environment, Properties properties); 7 public SqlSessionFactory build(InputStream inputStream); 8 public SqlSessionFactory build(InputStream inputStream, String environment); 9 public SqlSessionFactory build(InputStream inputStream, Properties properties); 10 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties); 11 public SqlSessionFactory build(Configuration config); 12 }
经过阅读源码发现生成SqlSessionFactory的简要步骤以下:
经过Reader,XMLConfigBuilder,Configuration和DefaultSqlSessionFactory共同协做把SqlSessionFactory弄出来了。若是正式一点,用时序图画出来就是这样的:
承载MySql全部的配置Configuration类
下面重点关注的类是Configuration。在org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration中解析Configuration的代码以下,看完以后你就会以为很亲切,不少标签都用过:
1 private void parseConfiguration(XNode root) { 2 try { 3 //issue #117 read properties first 4 propertiesElement(root.evalNode("properties")); 5 Properties settings = settingsAsProperties(root.evalNode("settings")); 6 loadCustomVfs(settings); 7 typeAliasesElement(root.evalNode("typeAliases")); 8 pluginElement(root.evalNode("plugins")); 9 objectFactoryElement(root.evalNode("objectFactory")); 10 objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); 11 reflectorFactoryElement(root.evalNode("reflectorFactory")); 12 settingsElement(settings); 13 // read it after objectFactory and objectWrapperFactory issue #631 14 environmentsElement(root.evalNode("environments")); 15 databaseIdProviderElement(root.evalNode("databaseIdProvider")); 16 typeHandlerElement(root.evalNode("typeHandlers")); 17 mapperElement(root.evalNode("mappers")); 18 } catch (Exception e) { 19 throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); 20 } 21 }
话说Java的这个着色也太稀烂了。typeAliases,typeHandlers和mappers是否是很熟悉。而后Configuration的类图是:
从这个图中能够看出Configuration分为两个部分:
- 和数据库相关的部分
- 和文档解析有关的部分
再返回Configuration的时候,mybatis-config.xml中的各个元素都已经解析出来了:
Environment:数据源和事务管理器
Environment就是配置数据源的地方,在mybatis-config.xml中是这样的:
1 <!-- 配置mybatis运行环境 --> 2 <environments default="development"> 3 <environment id="development"> 4 <!-- type="JDBC" 表明使用JDBC的提交和回滚来管理事务 --> 5 <transactionManager type="JDBC" /> 6 <!-- mybatis提供了3种数据源类型,分别是:POOLED,UNPOOLED,JNDI --> 7 <!-- POOLED 表示支持JDBC数据源链接池 --> 8 <!-- UNPOOLED 表示不支持数据源链接池 --> 9 <!-- JNDI 表示支持外部数据源链接池 --> 10 <dataSource type="POOLED"> 11 <property name="driver" value="${jdbc.driver}" /> 12 <property name="url" value="${jdbc.url}" /> 13 <property name="username" value="${jdbc.username}" /> 14 <property name="password" value="${jdbc.password}" /> 15 </dataSource> 16 </environment> 17 </environments>
1 private void environmentsElement(XNode context) throws Exception { 2 if (context != null) { 3 if (environment == null) { 4 environment = context.getStringAttribute("default"); 5 } 6 for (XNode child : context.getChildren()) { 7 String id = child.getStringAttribute("id"); 8 if (isSpecifiedEnvironment(id)) { 9 TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); 10 DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); 11 DataSource dataSource = dsFactory.getDataSource(); 12 Environment.Builder environmentBuilder = new Environment.Builder(id) 13 .transactionFactory(txFactory) 14 .dataSource(dataSource); 15 configuration.setEnvironment(environmentBuilder.build()); 16 } 17 } 18 } 19 }
SqlSessionFactory类图
session中的主要几个类以下:
下面则是session相关的类图:
除了Closeable是java API中的一个接口以外,其余的都是MyBatis中的类或者接口。有好几个类我还不怎么熟悉,好比SqlSession,SqlSessionManager和Executor。可是这里先无论,直接来看session是如何获取的。