本篇文章為大家展示了MyBatis日志為什么可以兼容其他的日志框架,內(nèi)容簡明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過這篇文章的詳細(xì)介紹希望你能有所收獲。
從策劃到設(shè)計(jì)制作,每一步都追求做到細(xì)膩,制作可持續(xù)發(fā)展的企業(yè)網(wǎng)站。為客戶提供成都網(wǎng)站設(shè)計(jì)、網(wǎng)站建設(shè)、外貿(mào)網(wǎng)站建設(shè)、網(wǎng)站策劃、網(wǎng)頁設(shè)計(jì)、域名注冊(cè)、網(wǎng)站空間、網(wǎng)絡(luò)營銷、VI設(shè)計(jì)、 網(wǎng)站改版、漏洞修補(bǔ)等服務(wù)。為客戶提供更好的一站式互聯(lián)網(wǎng)解決方案,以客戶的口碑塑造優(yōu)易品牌,攜手廣大客戶,共同發(fā)展進(jìn)步。前言
日志,在我們開發(fā)中是一個(gè)非常重要的話題,良好的日志打印可以幫助我們快速的定位問題,可能現(xiàn)在我們開發(fā)用到最多的日志框架就是slf4j了,但是日志還有其他很多優(yōu)秀的框架,比如:Apache Common Log,Log4j,java.util.logging等。MyBatis作為一款優(yōu)秀的ORM框架,定義了一套統(tǒng)一的日志接口供應(yīng)用層調(diào)用,而底層卻利用適配器模式兼容了我們上面所列出來的常用日志框架。
MyBatis日志分類
在介紹MyBatis的全局配置文件的時(shí)候,我們提到setting內(nèi)有一個(gè)屬性logImpl,可以配置的選項(xiàng)有:SLF4J,LOG4J,LOG4J2,JDK_LOGGING,COMMONS_LOGGING,STDOUT_LOGGING,NO_LOGGING。
這就說明MyBatis支持六種日志類型(NO_LOGGING是不打印日志)。我們看一下MyBatis的日志模塊也可以很明顯的看出六種日志類型:
它們的對(duì)應(yīng)關(guān)系為:
日志屬性 | 對(duì)應(yīng)日志模塊包名 | 實(shí)現(xiàn)方式 |
---|---|---|
SLF4J | slf4j | 使用SLF4J日志框架實(shí)現(xiàn) |
LOG4J | log4j | 使用Log4J日志框架實(shí)現(xiàn)(1.x版本) |
LOG4J2 | log4j2 | 使用Log4J日志框架實(shí)現(xiàn)(2.x版本) |
JDK_LOGGING | jdk14 | 使用java.util.logging實(shí)現(xiàn) |
COMMONS_LOGGING | commons | 使用Apache Commons Logging實(shí)現(xiàn) |
STDOUT_LOGGING | stdout | 使用System類實(shí)現(xiàn) |
NO_LOGGING | nologging | 不打印日志 |
PS:需要注意的是,SLF4J并不是一個(gè)具體的日志框架,也就是我們不能單獨(dú)只配置SLF4J而不引入其他任何具體的日志框架。
簡單談?wù)凷LF4J
SLF4J:簡單日記門面。(英文全稱為simple logging Facade for Java),這個(gè)是用來為各種日志框架提供一個(gè)簡單的統(tǒng)一的接口,這樣使得我們?cè)谇袚Q日志框架的時(shí)候可以直接替換jar包就可以了,而無需修改源代碼。
logback我想大家都用過,logback是一個(gè)實(shí)現(xiàn)了具體日志打印的框架,但是MyBatis上面列出來的分類并沒有支持logback,它又為什么能夠打印呢?這就是SLF4J的作用了,因?yàn)閘ogback也實(shí)現(xiàn)了SLF4J提供的接口,所以我們需要將logback和SLF4J結(jié)合配置使用才行。而后面的介紹中也可以看到,MyBatis中如果我們不指定日志種類的時(shí)候,優(yōu)先選擇的就是SLF4J,這正是因?yàn)镾LF4J可以和其他許多日志框架一起結(jié)合來使用。
那么假如我們指定了日志類型為SLF4J,但是不引入其他任何實(shí)現(xiàn)呢?
答案就是MyBatis不會(huì)打印任何日志出來,下圖就是只配置了SLF4J而沒有引入其他任何實(shí)現(xiàn)的警告信息:
可以看到這里提示我們SLF4J沒有任何實(shí)現(xiàn),而后面的sql語句和參數(shù)這些信息也沒有打印出來。
MyBatis日志實(shí)現(xiàn)原理
日志的解析
老規(guī)矩,我們還是先找到加載mybatis-config配置文件中的解析日志的源碼:
這里首先會(huì)根據(jù)我們配置的屬性作為別名去TypeAliasRegistry類中查找對(duì)應(yīng)的類,如果不存在這個(gè)別名,那就會(huì)把我們配置的屬性直接通過Class.forName去查找日志類,所以看到這里就明白我們可以自定義日志類,只要實(shí)現(xiàn)Log接口就行,然后配置我們自己的類名就行了。雖然別名都存在TypeAliasRegistry類里面,但是我們前面介紹MyBatis配置文件的時(shí)候,列出了TypeAliasRegistry類中默認(rèn)初始化的別名,并沒有看到日志相關(guān)類的別名,那么日志的別名又是在哪里配置的呢?我們打開Configuration類:
可以看到Configuration的構(gòu)造方法里面也初始化了一些別名注冊(cè)到TypeAliasRegistry類了。
接下來我們看看讀取到日志類之后調(diào)用了setLogImpl做了什么事情:
調(diào)用了LogFactory類的方法。
LogFactory
LogFactory工廠是負(fù)責(zé)創(chuàng)建日志對(duì)象對(duì)應(yīng)的適配器。
LogFactory的靜態(tài)代碼塊內(nèi)按順序初始化了所有內(nèi)置的日志
再看一下tryImplementation方法,如果logConstructor不為空,說明當(dāng)前還沒有加載到日志適配器,那就繼續(xù)執(zhí)行run()方法,也就是繼續(xù)執(zhí)行useXXXLogging方法,而所有的useXXXLogging方法都是調(diào)用了setImplementation方法。
下面這里如果加載成功之后就會(huì)對(duì)logConstructor進(jìn)行賦值,那么后續(xù)的方法就不會(huì)再執(zhí)行run()方法, 而如果拋出異常,因?yàn)橐呀?jīng)被捕獲了,所以就會(huì)繼續(xù)往后執(zhí)行靜態(tài)代碼塊內(nèi)的方法。
從上面的LogFactory中我們可以看到,初始化的時(shí)候就會(huì)默認(rèn)初始化一個(gè)日志適配器,所以如果我們引用了相關(guān)日志所需要的類,那么就會(huì)按照static代碼塊內(nèi)的順序進(jìn)行選擇一個(gè)合適的日志適配器。
繼續(xù)回到上面的Configuration里面,這里拿到我們配置的日志信息之后,會(huì)直接調(diào)用useCustomLogging方法,也就是繞過了上面的logConstructor == null這個(gè)判斷,而直接調(diào)用了setImplementation方法,所以假如我們配置了日志信息,那么會(huì)覆蓋初始化的日志適配器。
PS:假如我們配置了一個(gè)不存在的日志類,那么因?yàn)樗侵苯诱{(diào)用setImplementation方法,所以異常就會(huì)被拋出來,不拋異常的方法是tryImplementation而不是setImplementation。
jdbc log
MyBatis的日志包下面還有一個(gè)包時(shí)jdbc,這個(gè)我們還沒有介紹,那么jdbc包下面的類又有什么用呢?我們先看一下類圖關(guān)系:
很明顯,MyBatis將日志拆分成了ConnectionLogger,PreparedStatementLogger,ResultSetLogger,StatementLogger四種類型分開處理,它們都繼承了BaseJdbcLogger類,而且實(shí)現(xiàn)了InvocationHandler接口,也很明顯,這里用到了JDK動(dòng)態(tài)代理。
任意點(diǎn)開ConnectionLogger可以發(fā)現(xiàn),它是用來代理Connection對(duì)象的:
其他三個(gè)那很明顯,分別是用來代理PreparedStatement,ResultSet,Statement這三個(gè)對(duì)象的。也就是說MyBatis中日志最終的打印是通過JDK動(dòng)態(tài)代理來實(shí)現(xiàn)的,而且不同的執(zhí)行過程分成了四個(gè)對(duì)象來分別負(fù)責(zé)對(duì)應(yīng)的日志打印。
我們繼續(xù)看一下ConnectionLogger的invoke方法,可以看到,這里就是打印了一句日志:
上面日志打印出來的效果就是我們下面紅框中的日志:
等等,差點(diǎn)被忽悠了,這代碼里面并沒有打印“==>”,打印出來的這個(gè)符號(hào)又是怎么來的呢?
那就需要進(jìn)入debug方法里面繼續(xù)看一看,這個(gè)debug方法是在抽象類BaseJdbcLogger里面實(shí)現(xiàn)的,所以我們還需要看看BaseJdbcLogger類的debug方法。
可以看到這里打印的時(shí)候拼接了一個(gè)前綴:
PS:queryStack是查詢層數(shù),如果沒有嵌套查詢則queryStack=1
上述內(nèi)容就是MyBatis日志為什么可以兼容其他的日志框架,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。