本篇就是記流水賬的,一般孩兒可忍不了
在前面一篇博客中,我從官方文檔上抄了這么一段內(nèi)容,它是一段完整的mybatis執(zhí)行步驟的代碼啊犬。首先創(chuàng)建sqlSessionFactory ,在利用它獲取一個(gè)SqlSession對(duì)象壁查,這個(gè)SqlSession對(duì)象通過getMapper方法獲取一個(gè)MapperProxy的代理對(duì)象椒惨,并利用代理對(duì)象執(zhí)行增刪改查的邏輯。
String resource = "mapper/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
try{
BlogMapper mapper = sqlSession.getMapper(XXXX.class)
mapper.....
}finally{
sqlSession.close();
}
SqlSessionFactory
SqlSessionFactoryBuilder.build(resource) 的方法
創(chuàng)建SqlSessionFactory階段最重要的就是解析所有內(nèi)容潮罪。第一個(gè)Parser類解析全局配置文件康谆,存入Configuration對(duì)象中领斥。在解析所有的mapper.xml內(nèi)容,將每一個(gè)表示SQL的語句包裝成一個(gè)MappedStatement對(duì)象沃暗,這個(gè)對(duì)象包含了SQL語句的所有信息月洛。并將所有的MappedStatement存放到Configuration中,用一個(gè)全類名Mapper.id的形式(如com.zengg.test.dao.EmployeeMapper.getEmpById)的形式一一對(duì)應(yīng)孽锥,并生成一個(gè)DefaultSqlSessionFactory對(duì)象嚼黔,構(gòu)造器存入Configuration。
此外呢惜辑,Configuration還有一個(gè)屬性MapperRegistry唬涧,這個(gè)里面一個(gè)namespace的接口類對(duì)應(yīng)一個(gè)MapperProxyFactory,相當(dāng)于以后的MapperProxy是用這個(gè)工廠生產(chǎn)的盛撑。
1碎节、首先會(huì)創(chuàng)建一個(gè)XMLConfigBuilder的類對(duì)象,顧名思義抵卫,它的作用是解析全局配置文件的類狮荔,并執(zhí)行解析方法。
2介粘、XMLConfigBuilder.parse()方法中的parseConfiguration(XNode root)方法同根節(jié)點(diǎn)開始殖氏,將所有可能涉及到的配置標(biāo)簽如properties、typeAliases等信息全部封裝到Configuration對(duì)象中姻采,包括Setting里面的值雅采,沒有配置的就使用默認(rèn)值填充
3、然后返回這個(gè)Configuration 對(duì)象并創(chuàng)建一個(gè)new DefaultSqlSessionFactory(config)
到這兒SqlSessionFactory就創(chuàng)建好了慨亲,它只是獲取了全局配置文件的內(nèi)容并新建默認(rèn)對(duì)象DefaultSqlSessionFactory总滩。
SqlSession
SqlSessionFactory.openSession()
這個(gè)階段主要是生成一個(gè)Executor的執(zhí)行器,根據(jù)configuration中的不同的executorType巡雨,生成不一樣的執(zhí)行器(REUSE/SIMPLE(默認(rèn))/BATCH)。如果有緩存席函,再包裝一次成CacheExecutor(實(shí)際還是用之前生成的執(zhí)行器操作铐望,包裝了一層緩存而已),然后將Executor和Configuration作為構(gòu)造器的一部分茂附,生成一個(gè)DefaultSqlSession 對(duì)象
1正蛙、在DefaultSqlSessionFactory類中執(zhí)行openSession()方法,這個(gè)方法首先從全局配置對(duì)象Configuration中獲取Environment標(biāo)簽的信息中的事務(wù)管理器和ExecutorType营曼,并創(chuàng)建一個(gè)重要對(duì)象Executor(執(zhí)行器)乒验,不同的ExecutorType執(zhí)行器類型,會(huì)創(chuàng)建不同的執(zhí)行器蒂阱。
2锻全、如果我們開啟了二級(jí)緩存狂塘,mybatis會(huì)將已經(jīng)創(chuàng)建的執(zhí)行器包裝成一個(gè)新的擴(kuò)展執(zhí)行器對(duì)象CachingExecutor。這個(gè)緩存執(zhí)行器里面的通用的邏輯依然是用的之前創(chuàng)建的執(zhí)行器鳄厌,不過是在外加載了一層緩存相關(guān)處理的方法荞胡。
3、將Configuration了嚎、Executor 封裝創(chuàng)建一個(gè)DefaultSqlSession對(duì)象并返回泪漂。
MapperProxy
sqlSession.getMapper(Class<T> type)
重要的就是使用MapperProxyFactory創(chuàng)建了MapperProxy對(duì)象,對(duì)象里包含了DefaultSqlSession(Executor和configuration)的內(nèi)容
1歪泳、getMapper()方法實(shí)際上是調(diào)用了Configuration的getMapper方法(傳入了sqlSession對(duì)象作為參數(shù))萝勤,再調(diào)用MapperRegistry類對(duì)象去創(chuàng)建。
2呐伞、在MapperReistry類中敌卓,顯示根據(jù)參數(shù)MapperDao的全類名為key,獲取到對(duì)應(yīng)的MapperProxyFactory荸哟,然后在利用這個(gè)MapperProxyFactory新建實(shí)例MapperProxy假哎,這個(gè)MapperProxy就是我們用的代理對(duì)象了。它底層是利用了JDK下的java.lang.reflect包去實(shí)現(xiàn)的鞍历。
執(zhí)行查詢
匹配MappedStatement舵抹,拿到語句的內(nèi)容,為查詢做準(zhǔn)備劣砍。執(zhí)行查詢的邏輯由SqlSession執(zhí)行(包含了Executor和Configuration對(duì)象)
1钟哥、MapperProxy實(shí)現(xiàn)了接口InvocationHandler,它執(zhí)行方法的時(shí)候新進(jìn)入它的invoke()方法给涕。這個(gè)方法中傳入三個(gè)參數(shù)垛叨,分別是proxy對(duì)象、方法接口以及參數(shù)装畅。
?????然后創(chuàng)建一個(gè)MapperMethod對(duì)象并調(diào)用execute方法靠娱。
2、根據(jù)當(dāng)前方法的類型(增刪改查)分別執(zhí)行不同的邏輯
3掠兄、當(dāng)前跟蹤查詢方法像云,不同的返回參數(shù)有不同的MappedStatement執(zhí)行邏輯(分頁、查詢條件)蚂夕,比如說返回值為map迅诬,那么執(zhí)行結(jié)果就是 (SqlSession執(zhí)行查詢)
result = sqlSession.selectList(this.command.getName(), param);
如果返回值就是一個(gè)數(shù),也會(huì)返回一個(gè)集合婿牍,只不過會(huì)再去集合的第一個(gè)值侈贷,因此它們執(zhí)行查詢的邏輯基本相似。
以最下面的selectOne為例(返回值為單個(gè)對(duì)象)等脂,繼續(xù)跟蹤俏蛮。繼續(xù)執(zhí)行DefaultSqlSession的查詢類撑蚌,返回值為List并只取第一個(gè)作為結(jié)果返回。"參數(shù)中statement實(shí)際上就是sql的唯一標(biāo)識(shí)全類名加配置語句的唯一id")
4嫁蛇、利用封裝在DefaultSqlSession中的執(zhí)行器Executor 進(jìn)行查詢邏輯锨并。Executor有兩個(gè)實(shí)現(xiàn)類,一個(gè)是基礎(chǔ)實(shí)現(xiàn)類BaseExecutor睬棚,另外一個(gè)是CachingExecutor緩存執(zhí)行器第煮。這里我進(jìn)入緩存執(zhí)行器查看邏輯。
先回從configuration中對(duì)比傳入的statement參數(shù)抑党,拿到唯一的MappedStatement對(duì)象(這個(gè)對(duì)象封裝了該執(zhí)行語句的所有配置信息)
Executor
1包警、接續(xù)上面的邏輯,SqlSession調(diào)用Executor去執(zhí)行的query的方法底靠,這個(gè)方法第一步就會(huì)生成一個(gè)BoundSql的對(duì)象害晦,這個(gè)對(duì)象的作用應(yīng)該是先對(duì)我們的傳參作一些封裝處理(包含sql語句、傳參暑中、傳參類型等等)壹瘟。然后在創(chuàng)建了一個(gè)為查詢或者保存緩存用的CacheKey對(duì)象,這玩意兒有點(diǎn)長(方法id,sql語句鳄逾,參數(shù)信息等等)稻轨,感覺啥都包含了,就是為了確認(rèn)查詢的唯一性的雕凹。
2殴俱、之后先執(zhí)行檢查緩存中有沒有,沒有的話使用再調(diào)用被CacheExecutor包裝的真正的Executor執(zhí)行查詢
query方法
3枚抵、從本地緩存中拿數(shù)據(jù)(一級(jí)緩存)线欲,沒有的話再重新執(zhí)行查詢
4、后面一直跟蹤到SimpleExecutor的執(zhí)行器的doQuery方法(查詢完了之后的結(jié)果又會(huì)存放在localCache本地緩存中)汽摹。首先定義了一個(gè)JDBC原生的Statemten對(duì)象李丰,也說明了底層就是根據(jù)JDBC完成的。此外逼泣,這個(gè)方法出現(xiàn)了非常重要的第二個(gè)接口對(duì)象StatementHandler(可使用攔截器攔截)趴泌。(注:在創(chuàng)建StatementHandler的時(shí)候,構(gòu)造器中會(huì)默認(rèn)創(chuàng)建另外兩個(gè)重要對(duì)象PrepareHandler和ResultSethandler (BaseStatementHandler 抽象類中實(shí)現(xiàn)的))
StatementHandler ---> RoutingStatementHandler --> 默認(rèn)PreparedStatementHandler
5圾旨、在上圖中,使用prepareStatement()創(chuàng)建Statement對(duì)象中魏蔗,需要先創(chuàng)建鏈接砍的,然后使用RoutingStatementHandler進(jìn)行參數(shù)預(yù)編譯。這個(gè)預(yù)編譯又依賴第三個(gè)特殊對(duì)象PrepareHandler對(duì)象進(jìn)行輔助莺治。 而PrepareHandler 又使用TypeHandler 進(jìn)行參數(shù)的設(shè)置廓鞠。
6帚稠、第四點(diǎn)中的圖片,執(zhí)行最后一步query后床佳,返回的結(jié)果滋早,又需要第四個(gè)非常重要的對(duì)象去處理返回的結(jié)果(方法在PreparedStatementHandler中),同樣也用了TypeHandler輔助執(zhí)行砌们。
文中的四大對(duì)象Executor杆麸、StatementHandler、PrepareHandler和ResultSetHandler 都有一句interceptorChain.pluginAll(target)的方法用于包裝它們浪感,這也是實(shí)現(xiàn)Mybatis的Plugin插件的切入點(diǎn)昔头。