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是如何獲取的。

相關文章
相關標籤/搜索