Mybatis源碼剖析 -- 初始化過程(傳統(tǒng)方式)

一、讀取配置文件,讀成字節(jié)輸入流,注意:現(xiàn)在還沒解析

  1. 入口使用Resources.getResourceAsStream()方法獲取字節(jié)輸入流
    public class MybatisTest {
        /**
         * 傳統(tǒng)方式
         * @throws IOException
         */
        @Test
        public void test1() throws IOException {
          // 1. 讀取配置文件,讀成字節(jié)輸入流,注意:現(xiàn)在還沒解析
          InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        }
    }
    
  2. 點進去看getResourceAsStream()其實傳了一個 null 的類加載器和核心配置文件的路勁下去
    public static InputStream getResourceAsStream(String resource) throws IOException {
        return getResourceAsStream(null, resource);
    }
    
  3. 繼續(xù)往下點,又調(diào)用了classLoaderWrapper.getResourceAsStream()
    public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
        InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
        if (in == null) {
            throw new IOException("Could not find resource " + resource);
        }
        return in;
    }
    
  4. 接著往下看,最終在 ClassLoaderWrapper 類中找到了類加載器和真正讀成字節(jié)流的方法
    public InputStream getResourceAsStream(String resource, ClassLoader classLoader) {
        return getResourceAsStream(resource, getClassLoaders(classLoader));
    }
    
    ClassLoader[] getClassLoaders(ClassLoader classLoader) {
        return new ClassLoader[]{
                classLoader,
                defaultClassLoader,
                Thread.currentThread().getContextClassLoader(),
                getClass().getClassLoader(),
                systemClassLoader};
    }
    
    InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
        // 遍歷 ClassLoader 數(shù)組
        for (ClassLoader cl : classLoader) {
            if (null != cl) {
                // 獲得 InputStream ,不帶 /
                // try to find the resource as passed
                InputStream returnValue = cl.getResourceAsStream(resource);
                // now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource
                // 獲得 InputStream ,帶 /
                if (null == returnValue) {
                    returnValue = cl.getResourceAsStream("/" + resource);
                }
    
                // 成功獲得到,返回
                if (null != returnValue) {
                    return returnValue;
                }
            }
        }
        return null;
    }
    

二、解析配置文件,封裝 Configuration 對象,創(chuàng)建 DefaultSqlSessionFactory 對象

  1. api入口,使用構(gòu)建者模式創(chuàng)建一個 SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
  2. 點進去之后發(fā)現(xiàn),其實調(diào)用了一個重載的方法,傳遞三個參數(shù),除了配置文件的字節(jié)流之外,其余都傳了 null 值
    // 我們最初調(diào)用的build
    public SqlSessionFactory build(InputStream inputStream) {
        //調(diào)用了重載方法
        return build(inputStream, null, null);
    }
    
  3. 點進去查看這個重載方法
    // 調(diào)用的重載方法
    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
            // 創(chuàng)建 XMLConfigBuilder, XMLConfigBuilder是專門解析mybatis的配置文件的類
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            // 執(zhí)行 XML 解析
            // 創(chuàng)建 DefaultSqlSessionFactory 對象
            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.
            }
        }
    }
    
  4. 點進parser.parse()方法,看一下到底 Mybatis 它是怎么解析配置文件的
    /**
     * 解析 XML 成 Configuration 對象。
     *
     * @return Configuration 對象
     */
    public Configuration parse() {
        // 若已解析,拋出 BuilderException 異常
        if (parsed) {
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        }
        // 標記已解析
        parsed = true;
        ///parser是XPathParser解析器對象,讀取節(jié)點內(nèi)數(shù)據(jù),<configuration>是MyBatis配置文件中的頂層標簽
        // 解析 XML configuration 節(jié)點
        parseConfiguration(parser.evalNode("/configuration"));
        return configuration;
    }
    
  5. 先獲取一個頂層的 configuration 節(jié)點,然后調(diào)用parseConfiguration()這個方法,可以看到,這里就是對各種標簽進行解析
    /**
     * 解析 XML
     *
     * 具體 MyBatis 有哪些 XML 標簽,參見 《XML 映射配置文件》http://www.mybatis.org/mybatis-3/zh/configuration.html
     *
     * @param root 根節(jié)點
     */
    private void parseConfiguration(XNode root) {
        try {
            //issue #117 read properties first
            // 解析 <properties /> 標簽
            propertiesElement(root.evalNode("properties"));
            // 解析 <settings /> 標簽
            Properties settings = settingsAsProperties(root.evalNode("settings"));
            // 加載自定義的 VFS 實現(xiàn)類
            loadCustomVfs(settings);
            // 解析 <typeAliases /> 標簽
            typeAliasesElement(root.evalNode("typeAliases"));
            // 解析 <plugins /> 標簽
            pluginElement(root.evalNode("plugins"));
            // 解析 <objectFactory /> 標簽
            objectFactoryElement(root.evalNode("objectFactory"));
            // 解析 <objectWrapperFactory /> 標簽
            objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            // 解析 <reflectorFactory /> 標簽
            reflectorFactoryElement(root.evalNode("reflectorFactory"));
            // 賦值 <settings /> 到 Configuration 屬性
            settingsElement(settings);
            // read it after objectFactory and objectWrapperFactory issue #631
            // 解析 <environments /> 標簽
            environmentsElement(root.evalNode("environments"));
            // 解析 <databaseIdProvider /> 標簽
            databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            // 解析 <typeHandlers /> 標簽
            typeHandlerElement(root.evalNode("typeHandlers"));
            // 解析 <mappers /> 標簽
            mapperElement(root.evalNode("mappers"));
        } catch (Exception e) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
    }
    
  6. 重點看一個 properties 標簽到底是怎么解析的吧,剩下的觸類旁通
    /**
     * 1. 解析 <properties /> 標簽,成 Properties 對象。
     * 2. 覆蓋 configuration 中的 Properties 對象到上面的結(jié)果。
     * 3. 設(shè)置結(jié)果到 parser 和 configuration 中
     *
     * @param context 節(jié)點
     * @throws Exception 解析發(fā)生異常
     */
    private void propertiesElement(XNode context) throws Exception {
        if (context != null) {
            // 讀取子標簽們,為 Properties 對象
            Properties defaults = context.getChildrenAsProperties();
            // 讀取 resource 和 url 屬性
            String resource = context.getStringAttribute("resource");
            String url = context.getStringAttribute("url");
            if (resource != null && url != null) { // resource 和 url 都存在的情況下,拋出 BuilderException 異常
                throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");
            }
            // 讀取本地 Properties 配置文件到 defaults 中。
            if (resource != null) {
                defaults.putAll(Resources.getResourceAsProperties(resource));
                // 讀取遠程 Properties 配置文件到 defaults 中。
            } else if (url != null) {
                defaults.putAll(Resources.getUrlAsProperties(url));
            }
            // 覆蓋 configuration 中的 Properties 對象到 defaults 中。
            Properties vars = configuration.getVariables();
            if (vars != null) {
                defaults.putAll(vars);
            }
            // 設(shè)置 defaults 到 parser 和 configuration 中。
            parser.setVariables(defaults);
            configuration.setVariables(defaults);
        }
    }
    
  7. 解析完成之后,返回一個 configuration 對象,該對象中包含了一個 mappedStatements,其數(shù)據(jù)結(jié)構(gòu)就是一個 map,根據(jù) namespace.id 存放一個 MappedStatement 對象,之前的自定義持久層框架也是借鑒了這個思路
    /**
     * MappedStatement 映射
     *
     * KEY:`${namespace}.${id}`
     */
    protected final Map<String, MappedStatement> mappedStatements = new StrictMap<>("Mapped Statements collection");
    
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容