1. 程式人生 > >深入Mybatis原始碼——配置解析

深入Mybatis原始碼——配置解析

@[toc] # 前言 上一篇分析了Mybatis的基礎元件,Mybatis的執行呼叫就是建立在這些基礎元件之上的,那它的執行原理又是怎樣的呢?在往下之前不妨先思考下如果是你會怎麼實現。 # 正文 熟悉Mybatis的都知道,在使用Mybatis時需要配置一個**mybatis-config.xml**檔案,另外還需要定義Mapper介面和Mapper.xml檔案,在config檔案中引入或掃描對應的包才能被載入解析(現在由於大多是SpringBoot工程,基本上都不會配置config檔案,而是通過註解進行掃描就行了,但本質上的實現和xml配置沒有太大區別,所以本篇仍以xml配置方式進行分析。),所以Mybatis的第一個階段必然是要去**載入並解析配置檔案**,這個階段在專案啟動時就應該完成,後面直接呼叫即可。載入完成之後,自然就是等待呼叫,但是我們在專案中只會定義Mapper介面和Mapper.xml檔案,那具體的實現類在哪呢?Mybatis是通過**動態代理**實現的,所以第二個階段應該是**生成Mapper介面的代理實現類**。通過呼叫代理類,最終會生成對應的sql訪問資料庫並獲取結果,所以最後一個階段就是**SQL解析**(引數對映、SQL對映、結果對映)。本文主要分析配置解析階段。 ## 配置解析 Mybatis可以通過下面的方式解析配置檔案: ```java final String resource = "org/apache/ibatis/builder/MapperConfig.xml"; final Reader reader = Resources.getResourceAsReader(resource); SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader); ``` 所以入口就是**build**方法(從名字可以看出使用了**建造者模式**,它和**工廠模式**一樣,也是解用於建立物件的一種模式,不過與**工廠模式**不一樣的是,前者需要我們自己參與構建的細節,而後者則不需要): ```java public SqlSessionFactory build(Reader reader, String environment, Properties properties) { try { //讀取配置檔案 XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); return build(parser.parse());//解析配置檔案得到configuration物件,並返回SqlSessionFactory } 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. } } } ``` 這裡先是建立了一個**XMLConfigBuilder**物件,這個物件就是用來載入解析config檔案的,先看看它的構造方法中做了些什麼事情: ```java public XMLConfigBuilder(Reader reader, String environment, Properties props) { this(new XPathParser(reader, 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; } ``` 需要注意的是這裡建立了一個**Configuration**物件,他就是Mybatis的核心CPU,儲存了所有的配置資訊,在後面的執行階段所需要的資訊都是從這個類取的,因為這個類比較大,這裡就不貼詳細程式碼了,讀者請務必閱讀原始碼熟悉該類。因為這個類物件儲存了所有的配置資訊,那麼必然這個類是全域性單例的,事實上這個物件的建立也只有這裡一個入口,保證了全域性唯一。 在該類的構造方法中,首先就註冊了核心元件的別名和對應的類對映關係: ```java public Configuration() { typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class); typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class); typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class); typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class); typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class); ......省略 languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class); languageRegistry.register(RawLanguageDriver.class); } ``` 而註冊類在例項化時同樣也註冊了一些基礎型別的別名對映: ```java public TypeAliasRegistry() { registerAlias("string", String.class); registerAlias("byte", Byte.class); registerAlias("long", Long.class); registerAlias("short", Short.class); registerAlias("int", Integer.class); registerAlias("integer", Integer.class); registerAlias("double", Double.class); registerAlias("float", Float.class); registerAlias("boolean", Boolean.class); ......省略 registerAlias("ResultSet", ResultSet.class); } ``` 看到這相信你就知道parameterType和resultType屬性的簡寫是怎麼實現的了。回到主流程,進入到**parser.parse**方法中: ```java 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 { //issue #117 read properties first