mybatis源碼解析 - 核心基礎組件之日誌組件

mybatis做爲當前主流的ORM框架之一,其流行程度遠超過了JPA,Hibernate,Bee等其它三方ORM框架,尤爲是在與Spring無縫黏合以後。最近至關一段時間,對mybatis的源碼(v3.5.6)和設計進行了一些研究,接下來會分章節給你們分享。java

總體設計架構sql

image.png

核心門面接口數據庫

SqlSession:做爲訪問數據庫的門面(或外觀),其對外屏蔽了經過mybatis數據庫訪問複雜度,大大下降了外部程序對mybatis的內部代碼依賴符合單一職責和迪米特法則,同時又提供了統一的訪問入口和能力。編程

SqlSessionFactory:SqlSession的工廠模式封裝, 其默認實現爲DefaultSqlSessionFactory類;設計模式

SqlSessionFactoryBuilder: 全局的SqlSessionFactory工廠建立,內部經過build()方法建立出SqlSessionFactory的具體工廠,其惟一的職責就是對build的各類重載,支持以各類外部形式建立SqlSessionFactory對象。緩存

核心處理層安全

配置文件解析:主要負責解析mybatis-config.xml全局配置文件,其中包括:properties, settings, typeAliases,typeHandlers,plugins,environments,mappers等主要部分。解析的結果分塊緩存之Configuration全局對象,後面此對象講貫穿整個框架始終。mybatis

參數映射:經過ParamNameResolver, 解析mapper接口參數包括參數名,參數值並緩存參數的順序關係。架構

SQL解析:解析SQL語句, 預編譯SQL PrepareStatement分兩階段解析SQL, 第一階段在mybatis-config.xml配置文件加載時,將SQL相關的信息解析到MappedStatement對象中(預編譯語句,將#{} 替換成 ?),最終緩存進Configuration對象中;第二階段發生SQL執行階段,將預編譯語句中問號替換成最終的SQL語句。app

SQL執行:經過Executor執行組件做爲入口,內部調用RoutingStatementHandler路由接口,將請求最終路由到特定的StatementHandler中,最終調用原生jdbc PrepareStatement完成數據庫讀寫。

結果集映射: 對原生的ResultSet的解析和轉換成POJO對象的過程。

插件:mybatis中的插件,是繼承其內置的Interceptor接口,開發對這些接口對象的方法進行攔截加強:Executor,StatementHandler,ParameterHandler,ResultSetHandler。

基礎支撐層

數據源組件:對池化的數據庫鏈接進行建立和管理。

事務管理:mybatis的事務功能比較弱,基本都是基於jdbc的隔離、commit或rollback的簡單包裝,這塊通常和Spring的事務結合使用。

緩存組件:mybatis默認開啓一級緩存(也能夠在mapper.xml中關閉特定語句的一級緩存),它是基於SqlSession級的,而且是線程安全的。而二級緩存默認是不開啓的,須要在特定的mapper.xml中開啓,二級緩存的生命週期是應用程序級的,二級緩存的單位是namespace級別(也能夠在多個namespace中共享同一個緩存)。

binding組件:主要用於構建mybatis的Mapper編程模型(後面會詳細介紹)。

反射組件:在底層提供動態反射完整封裝,支撐反射對象及對象屬性、設置對象值等能力。關鍵部件:ObjectFactory,ObjectWrapper,ObjectWrapperFactory,ReflectorFactory,Reflector,

MetaClass,MetaObject等。

類型轉換:主要提供內置的jdbc數據庫類型和java程序數據類型,以及自定義的數據類型處理進行映射和轉換。

日誌組件:提供在mybatis中日誌打印能力。解決和集成衆多三方日誌組件包括:slf4j, commonslog, log4j2, log4j, jdklog等。而且按優先級(前面的優先級)動態掃描加載本地的日誌組件。

資源加載:提供對mybatis-config.xml、properties, mapper.xml,class等外部資源的掃描、緩存和加載能力。

解析器:在底層提供對配置文件、參數屬性、sql語句等提供解析能力。

今天首先分析一下mybatis的日誌組件。對於日誌組件我這邊是帶着以下問題去看源碼的:

  1. mybatis是否支持主流的日誌組件?從設計上,它是如何作到的?

  2. 如何兼容主流日誌組件,又能支撐mybatis自身的業務能力?

  3. 如何方便的提供日誌訪問、優先級和掃描機制 ?

  4. mybatis其它業務組件,是如何集成日誌組件開展業務的?

兼容三方日誌組件設計

mybatis自己不提供原生的日誌打印和存儲功能,它是靠適配其它第三方日誌組件來實現的。

image.png

如上圖所示,咱們能夠看出mybatis是經過適配其它三方的日誌組件實現它自身的Log業務接口的,接着看下面

image.png

image.png

從以上的代碼段截圖,咱們能夠清楚地得知,mybatis是採用了適配器設計模式,同時適配了slf4j, commonslog, log4j2, log4j, jdk等日誌組件。這些日誌組件自己所提供的接口和日誌級別都各不相同,而mybatis中須要的日誌業務能力是經過定義的Log接口來支持的。因此這裏須要將其它日誌的接口轉換成mybatis內置的業務和日誌定義,而同時又不能去改動第三方的日誌接口實現細節。那麼此時適配器模式就是最佳的選擇。適配的本質就是轉換。

image.png

提供統一訪問入口

那麼既然mybatis運用適配器模式,適配了這麼多三方日誌組件的接口。那麼它內部使用的時候,如何知道該使用那個組件來支撐業務 ? 根據什麼規則來選擇?又如何能知道使用的組件在咱們的應用程序中是否存在?顯然要解決這些問題mybatis須要單獨的設計。

image.png

它內部使用LogFactory工廠模式,在內部構建日誌對象。那它內部如何實現的呢?

image.png

在static構造器中對三方日誌適配接口進行按優先級選擇,優先級爲:slf4j ---> commonslog ----> log4j2 ----> log4j ---->jdklog , 採用這個優先級自動對工程中依賴的包進行掃描,發現有可用的包就採用,其它的日誌包忽略。

image.png

image.png

仔細看看以上代碼的截圖,你們是否就明白,它巧妙地採用了JDK8的特性,經過順序調用多個靜態方法來達到按優先級自動掃描肯定使用哪一個組件的設計目的

日誌組件的業務集成

OK, 既然組件和組件的入口的設計好了,那麼接下你們是否是更關注的問題是,mybatis設計的日誌功能究竟用在框架的哪些地方,是如何設計集成的,各個地方的做用是什麼?還有就是看看是否是和咱們平時在業務模塊中使用日誌是同樣呢 ?

首先要解決第一個問題,咱們要知道mybatis主要在哪些地方會打日誌。看下面,一個正常的mapper接口調用過程當中,日誌是如何輸出的。

image.png

從以上咱們能夠得知,mybatis在運行的時候,會在這三個關鍵的點打印各類日誌信息。OK, 那咱們就找到對應的源碼

image.png

先來看看ConnectionLogger,經過SQL鏈接數據庫成功時打印日誌的實現代碼

image.pngimage.png

上面截圖,你們都看得很清楚了吧?我就不用多說了。那麼咱們再來看看PreparestatementLogger代理實現過程代碼

image.pngimage.png

OK, 如上圖一切盡在不言中。最後咱們來看看ResultSetLogger對結果集是如何加強的

image.pngimage.png

怎麼樣,是否是So easy? 從以上的結果咱們能夠看出,mybatis是採用了Proxy代理技術,分別加強了Connection、Preparestatement、Statement以及ResultSet等對象,讓它具備了在特定位置打印個性化日誌的能力。看到了這裏,你們會不會想這些分散的Logger是否是該有一個總的調用入口呢?在mybatis中答案是確定的。看看下面這個位置

image.png

image.png

從MappedStatement賦予日誌能力到以上各個Logger代理加強的實際業務點,還會通過比較長封裝過程,後面會一一解析。如今暫時不展開這個點。


總結

從mybatis源碼中咱們能夠學到不少優秀的設計經驗、經典的設計模式和原則、設計銜接處理點巧妙運用的技巧。今天只談到了mybatis的日誌組件, 下次咱們將分享mybatis其它重要的基礎組件,請繼續關注!

相關文章
相關標籤/搜索