Mybatis源码之核心流程分析
終于談到了Mybatis最核心的東西了,最核心的就是通過(guò)配置XML文件或注解中的SQL,直接調(diào)用接口就能執(zhí)行配置好的SQL語(yǔ)句并封裝成對(duì)應(yīng)的返回類(lèi)型的數(shù)據(jù)。
先看一下Mybatis使用示例:
//創(chuàng)建Builder對(duì)象 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); //讀取配置文件IO流 InputStream is = Resources.getResourceAsStream("mybatis/mybatis-cfg.xml"); //解析IO流到內(nèi)存Configuration對(duì)象中 SqlSessionFactory build = builder.build(is); //獲取一個(gè)持久化會(huì)話對(duì)象 SqlSession openSession = build.openSession(); //通過(guò)SqlSession調(diào)用 List list = openSession.selectOne("com.test.mybatis.MybatisTest.official.dao.IUserDao.getUserInfo"); System.out.println("============"+list); //通過(guò)接口對(duì)象調(diào)用 IUserDao dao = openSession.getMapper(IUserDao.class); System.out.println(dao.getUserLimit());//關(guān)閉SqlSession openSession.close();雖然看起來(lái)使用非常簡(jiǎn)單,但是內(nèi)部構(gòu)建這么一個(gè)ORM框架還是很艱難的,所以本章節(jié)會(huì)特別長(zhǎng)
設(shè)計(jì)模式
照舊先來(lái)看一看設(shè)計(jì)模式
建造者模式(Builder Pattern)
使用多個(gè)簡(jiǎn)單的對(duì)象一步一步構(gòu)建成一個(gè)復(fù)雜的對(duì)象。這種類(lèi)型的設(shè)計(jì)模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對(duì)象的最佳方式。
一個(gè) Builder 類(lèi)會(huì)一步一步構(gòu)造最終的對(duì)象。該 Builder 類(lèi)是獨(dú)立于其他對(duì)象的。
UML:
Builder:給出一個(gè)抽象接口,以規(guī)范產(chǎn)品對(duì) 象的各個(gè)組成成分的建造。這個(gè)接口規(guī)定要實(shí) 現(xiàn)復(fù)雜對(duì)象的哪些部分的創(chuàng)建,并不涉及具體 的對(duì)象部件的創(chuàng)建;
ConcreteBuilder:實(shí)現(xiàn)Builder接口,針對(duì) 不同的商業(yè)邏輯,具體化復(fù)雜對(duì)象的各部分的 創(chuàng)建。 在建造過(guò)程完成后,提供產(chǎn)品的實(shí)例;
Director:調(diào)用具體建造者來(lái)創(chuàng)建復(fù)雜對(duì)象的 各個(gè)部分,在指導(dǎo)者中不涉及具體產(chǎn)品的信息, 只負(fù)責(zé)保證對(duì)象各部分完整創(chuàng)建或按某種順序 創(chuàng)建;
Product:要?jiǎng)?chuàng)建的復(fù)雜對(duì)象。
建造者模式對(duì)齊工廠模式創(chuàng)建出來(lái)的對(duì)象要更加的復(fù)雜,適用于實(shí)例化對(duì)象頻繁改變的場(chǎng)景,適合流式編程。
但是現(xiàn)在流式編程是趨勢(shì),所以使用建造者模式創(chuàng)建對(duì)象的框架會(huì)越來(lái)越多,下面會(huì)講到流式編程。
策略模式(Strategy Pattern)
一個(gè)類(lèi)的行為或其算法可以在運(yùn)行時(shí)更改。這種類(lèi)型的設(shè)計(jì)模式屬于行為型模式。
在策略模式中,我們創(chuàng)建表示各種策略的對(duì)象和一個(gè)行為隨著策略對(duì)象改變而改變的 context 對(duì)象。策略對(duì)象改變 context 對(duì)象的執(zhí)行算法。
應(yīng)用最多的就是if else代碼當(dāng)中
UML:
?
Context:算法調(diào)用者,使用setStrategy方法靈活的選擇策略(strategy);
Strategy:算法的統(tǒng)一接口;
ConcreteStrategy:算法的具體實(shí)現(xiàn);
源碼分析
構(gòu)建并解析XML文件
在使用的過(guò)程中都了解到了,首先就是將Mybatis的配置XML文件進(jìn)行解析,解析后的數(shù)據(jù)都是存于Configuration對(duì)象當(dāng)中,
先看一下Configuration這個(gè)類(lèi),單例,生命周期是應(yīng)用級(jí)別,屬性太多這里就不全截圖出來(lái)了,大家可以自己下載源碼去查看,截圖如下:
但是在這個(gè)類(lèi)中有幾個(gè)對(duì)象需先講一下:
MapperRegistry
mapper接口動(dòng)態(tài)代理工廠類(lèi)的注冊(cè)中心。在MyBatis中,通過(guò)mapperProxy實(shí)現(xiàn) InvocationHandler接口,MapperProxyFactory用于生成動(dòng)態(tài)代理的實(shí)例對(duì)象;
TypeAliasRegistry
所以別名存儲(chǔ)的地方,但是大家可以去看一下Configuration的構(gòu)造方法,各類(lèi)的默認(rèn)參數(shù)都已經(jīng)設(shè)置進(jìn)去,不僅僅是Configuration的構(gòu)造,在它自己的構(gòu)造中將jdk自帶的一些類(lèi)也加進(jìn)去了,這就是為什么有些別名默認(rèn)就有的原因:
這個(gè)類(lèi)里面也是有個(gè)Map<String,Class<?>>的這樣的map用來(lái)儲(chǔ)存這些數(shù)據(jù)
mappedStatements
這個(gè)是所有SQL信息儲(chǔ)存的地方,使用的事繼承自HashMap的自定義類(lèi)StrictMap,這個(gè)map的key值為mapper的XML的namespace加上select|update|delete|insert標(biāo)簽的ID。具體的下面會(huì)談到。
SqlSource
mapper.xml文件中的sql語(yǔ)句會(huì)被解析成SqlSource對(duì)象,經(jīng)過(guò)解析SqlSource包含的語(yǔ) 句最終僅僅包含?占位符,可以直接提交給數(shù)據(jù)庫(kù)執(zhí)行;
TypeHandlerRegistry
是將數(shù)據(jù)庫(kù)中的類(lèi)型轉(zhuǎn)換成JDK中的類(lèi)型,進(jìn)行對(duì)應(yīng),這個(gè)也有默認(rèn)的集合,大家也可以去源碼里面看一下。
整體介紹
整體解析建造者Builder類(lèi)關(guān)系圖:
使用的建造者圖解:
我們就跟著解析流程講解一下(在創(chuàng)建XMLConfigBuiler對(duì)象的時(shí)候會(huì)把Configuration對(duì)象創(chuàng)建出來(lái)):
進(jìn)XMLConfigBuilder對(duì)象的parse方法看,執(zhí)行了一個(gè)parseConfiguration(parser.evalNode("/configuration"));解析對(duì)象之前封裝的XNode節(jié)點(diǎn)數(shù)據(jù);
有些并非很重要的方法就沒(méi)有標(biāo)明,所以感興趣的可以去看一下。
解析properties標(biāo)簽
解析完后會(huì)將所有的數(shù)據(jù)設(shè)置在configuration對(duì)象當(dāng)中。
解析typeAliases標(biāo)簽
解析plugin標(biāo)簽
插件的使用這好像好沒(méi)有提到,等后面在說(shuō)吧。
解析mappers標(biāo)簽
這個(gè)算是解析文件里的一個(gè)比較中的東西
這時(shí)候開(kāi)始使用XMLMapperBuilder建造者開(kāi)始建造數(shù)據(jù)了:
我們看一下configurationElement方法:
這里的最后一個(gè)方法就開(kāi)始了XMLStatementBuilder建造者開(kāi)始建造了,看一些比較關(guān)鍵的地方
在創(chuàng)建MappedStatement對(duì)象時(shí),使用的是流式編程
將數(shù)據(jù)全部加載完后,就是綁定工作空間了
在綁定方法中,如果namespace不是指定的接口全類(lèi)名,那么就不會(huì)被添加到?mapperRegistry對(duì)象當(dāng)中,這個(gè)對(duì)象就是用來(lái)跟接口做綁定的,進(jìn)入到mapperRegistry的addMapper方法當(dāng)中,
基本就是這么將動(dòng)態(tài)代理跟namespace綁定的,構(gòu)造XML的過(guò)程基本完結(jié)了,使用Mybatis的初始化已經(jīng)完成,接下來(lái)就是使用持久化會(huì)話SqlSession這一塊了
創(chuàng)建SqlSession
SqlSession是Mybatis對(duì)外提供的一個(gè)重要接口,可以通過(guò)這個(gè)跟數(shù)據(jù)庫(kù)進(jìn)行交互,或者創(chuàng)建接口代理對(duì)象,
UML:
創(chuàng)建一個(gè)默認(rèn)的SqlSession工廠,這里用到了抽象工廠設(shè)計(jì)模式,之前為博客中提到了抽象工廠,那么這里就不細(xì)講了,
我們調(diào)用時(shí)創(chuàng)建出了一個(gè)默認(rèn)的DefaultSqlSession對(duì)象,在之前的示例當(dāng)中,我們可以直接調(diào)用selectList方法也能調(diào)用getMapper方法,其實(shí)直接使用SelectList方法是ibatis編程模型,當(dāng)加入了getMapper方法后就是采用接口形式開(kāi)發(fā),這種開(kāi)始模式比之前的ibatis開(kāi)發(fā)模式要更為簡(jiǎn)潔,并清楚業(yè)務(wù)。
我們先看一下原始的ibatis編程,即調(diào)用namespace+id方法
我們?cè)诳匆幌耮etMapper編程模式:
創(chuàng)建除了動(dòng)態(tài)代理類(lèi)之后,我們看一下代理類(lèi)的實(shí)現(xiàn):
這里的代理有一定的版本問(wèn)題,由于JDK8支持在接口中寫(xiě)方法主體,所以需做一層判斷才能執(zhí)行正確的方法。
又是在invoke中執(zhí)行的是invoke方法,所以在PlainMethodInvoker這個(gè)對(duì)象,執(zhí)行的是mapperMethod.execute,
所以由此看出,getMapper也是基于ibatis編程模式開(kāi)發(fā)。
SqlSession基本講完了,接下來(lái)就是Executor執(zhí)行器了。
Executor執(zhí)行器
這個(gè)用到是模板模式,我在AQS中談到的設(shè)計(jì)模式也是模板設(shè)計(jì)模式,模板模式-設(shè)計(jì)模式
BaseExecutor:抽象類(lèi),實(shí)現(xiàn)了executor接口的大部 分方法,主要提供了緩存管理和事務(wù)管理的能力,其他 子類(lèi)需要實(shí)現(xiàn)的抽象方法為:doUpdate,doQuery等方法;
這個(gè)有四個(gè)對(duì)應(yīng)的實(shí)現(xiàn)類(lèi):
SimpleExecutor:默認(rèn)配置,使用PrepareStatement對(duì)象訪問(wèn)數(shù)據(jù)庫(kù),每次訪問(wèn)都要?jiǎng)?chuàng)建新的 PrepareStatement對(duì)象;
ReuseExecutor:使用預(yù)編譯PrepareStatement對(duì)象訪問(wèn)數(shù)據(jù)庫(kù),訪問(wèn)時(shí),會(huì)重用緩存中的statement對(duì)象;
BatchExecutor:實(shí)現(xiàn)批量執(zhí)行多條SQL語(yǔ)句的能力;
ClosedExecutor:異常執(zhí)行器。
默認(rèn)的方法為SimpleExecutor(在Configuration參數(shù)中有),之前看到是執(zhí)行了Executor中的query方法,這里講一下:
進(jìn)入到queryFormDatabase方法中:
在doQuery方法中,發(fā)現(xiàn)多了Executor也并非執(zhí)行,讓Handler去執(zhí)行:
通過(guò)對(duì)SimpleExecutor doQuery()方法的解讀發(fā)現(xiàn),Executor是個(gè)指揮官,它在調(diào)度三個(gè)Handler工作:
StatementHandler:它的作用是使用數(shù)據(jù)庫(kù)的Statement或PrepareStatement執(zhí)行操作,啟承上啟下作用;
ParameterHandler:對(duì)預(yù)編譯的SQL語(yǔ)句進(jìn)行參數(shù)設(shè)置,SQL語(yǔ)句中的的占位符“?”都對(duì)應(yīng) BoundSql.parameterMappings集合中的一個(gè)元素,在該對(duì)象中記錄了對(duì)應(yīng)的參數(shù)名稱(chēng)以及該參數(shù)的相關(guān)屬性
ResultSetHandler:對(duì)數(shù)據(jù)庫(kù)返回的結(jié)果集(ResultSet)進(jìn)行封裝,返回用戶(hù)指定的實(shí)體類(lèi)型;
StatementHadnler
BaseStatementHandler:所有子類(lèi)的抽象 父類(lèi),定義了初始化statement的操作順序, 由子類(lèi)實(shí)現(xiàn)具體的實(shí)例化不同的statement (模板模式);
RoutingStatementHandler:Excutor組件 真正實(shí)例化的子類(lèi),使用靜態(tài)代理模式, 根據(jù)上下文決定創(chuàng)建哪個(gè)具體實(shí)體類(lèi);
SimpleStatmentHandler :使用statement 對(duì)象訪問(wèn)數(shù)據(jù)庫(kù),無(wú)須參數(shù)化;
PreparedStatmentHandler :使用預(yù)編譯 PrepareStatement對(duì)象訪問(wèn)數(shù)據(jù)庫(kù);
CallableStatmentHandler :調(diào)用存儲(chǔ)過(guò)程;
在獲取到對(duì)應(yīng)的Statement對(duì)象之后就是執(zhí)行Statement并封裝結(jié)果集
隨著Executor執(zhí)行器執(zhí)行完畢,數(shù)據(jù)也就這樣執(zhí)行出來(lái)了。
Mybatis大部分的核心知識(shí)基本都在這里,但是難免還是會(huì)有一些遺漏;后面遇到了就會(huì)做一些補(bǔ)充。
很多插件以及可擴(kuò)展的方法接口等等,Mybatis發(fā)展這么多年,不可能在一段時(shí)間內(nèi)知曉,還需潛心學(xué)習(xí)。
總結(jié)
以上是生活随笔為你收集整理的Mybatis源码之核心流程分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: git 无法访问
- 下一篇: JAVA中的native