你猜一下哪一個先執行?反正不要按常規來。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是如何獲取的。