SqlSessionFactory構建過程:原始碼簡要分析
簡述
在我們剛學習Mybatis,沒有整合Spring時,通常會以下面這種形式來進行SqlSessionFactory的建立
SqlSessionFactory sqlSessionFactory = null; String resource = "mybatis-config.xml"; InputStream inputStream; try { inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); }
通俗的講就是使用SqlSessionFactoryBuilder()的build()方法,並傳入配置檔案來生產SqlSessionFactory,下面我們通過閱讀原始碼簡要的瞭解以下整個流程
流程分析
【SqlSessionFactoryBuilder】
首先直接進入SqlSessionFactoryBuilder的build方法
在SqlSessionFactoryBuilder中,實現了多種不同的build方法,但大致可分為三類
- 以字元流(Reader)的形式讀取配置檔案
//核心,其他引數為Reader的方法都會最終指向本方法 public SqlSessionFactory build(Reader reader, String environment, Properties properties) { try { XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { reader.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } }
- 以位元組流(InputStream)的形式讀取配置檔案
//核心,其他引數為InputStream的方法都會最終指向本方法 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } }
-
直接以Configuration類例項作為入參
本方法是最核心的build方法,以上兩個方法最終都會指向本方法
public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }
通過上面三段程式碼我們可以瞭解到SqlSessionFactory構建的基本流程

流程
- 可以發現,在構建SqlSessionFactory時,最為重要的還是Configuration的建立
- 其實我們完全可以通過程式碼的方式去建立Configuration類的例項物件,然後直接呼叫public SqlSessionFactory build(Configuration config)方法去建立,但是這樣不管是可讀性還是修改程式碼的簡易程度都不如XML配置,因此我們選擇使用流的方式去讀取XML配置檔案然後解析為Configuration類
- 在從流到Configuration的過程中XMLConfigBuilder起到了至關重要的作用
【XMLConfigBuilder】
在SqlSessionFactory的build方法中,通過如下手段產生Configuration類
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); parser.parse();//parse方法生產Configuration例項物件
我們進入原始碼檢視流程
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) { this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props); } private XMLConfigBuilder(XPathParser parser, String environment, Properties props) { super(new Configuration()); ErrorContext.instance().resource("SQL Mapper Configuration"); this.configuration.setVariables(props); this.parsed = false; this.environment = environment; this.parser = parser; } public Configuration parse() { if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; parseConfiguration(parser.evalNode("/configuration")); return configuration; } private void parseConfiguration(XNode root) { try { Properties settings = settingsAsPropertiess(root.evalNode("settings")); //issue #117 read properties first propertiesElement(root.evalNode("properties")); loadCustomVfs(settings); typeAliasesElement(root.evalNode("typeAliases")); pluginElement(root.evalNode("plugins")); objectFactoryElement(root.evalNode("objectFactory")); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); reflectionFactoryElement(root.evalNode("reflectionFactory")); settingsElement(settings); // read it after objectFactory and objectWrapperFactory issue #631 environmentsElement(root.evalNode("environments")); databaseIdProviderElement(root.evalNode("databaseIdProvider")); typeHandlerElement(root.evalNode("typeHandlers")); mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }
- 大致流程就是XMLConfigBuilder讀取配置檔案流後,將其轉為XPathParser,XPathParser呼叫parse方法(呼叫parseConfiguration方法)
- parseConfiguration中完成了將配置檔案中的內容裝載到Configuration中,其中呼叫的各個方法內部最終都會使用configuration.setXXX的形式,將對應屬性進行裝載
建造者(Builder)模式
在整個SqlSessionFactory構建過程中,我們不難發現,其實就是如下思路
- 構建SqlSessionFactory需要構建大量的物件屬性,如environments、typeHandlers等等,構建這些內容也異常複雜,因此Mybatis提供了一個配置類Configuration對這些物件來進行統籌規劃,分步構建
- Configuration中所有內容構建完畢後,呼叫構造器XMLConfigBuilder和SqlSessionFactoryBuilder進行構造生產SqlSessionFactory
以上的設計模式被稱為建造者(Builder)模式,可以將一個產品的內部表象(屬性)與產品的生產過程分割開來,從而使一個建造過程生成具有不同的內部表象的產品物件
【如有錯誤,請指出!O(∩_∩)O謝謝】