MyBatis拦截器的执行顺序引起的MyBatis源码分析

你猜一下哪一个先执行?反正不要按常规来。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的生成

观察这两段代码引出了两个核心的类:SqlSessionFactorySession

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的简要步骤以下:

经过ReaderXMLConfigBuilderConfigurationDefaultSqlSessionFactory共同协做把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是如何获取的。

相关文章
相关标签/搜索