1.日志模塊需求
- 1)Mybatis沒有提供日志的實(shí)現(xiàn)類,需要接入第三方的日志組件,但第三方日志組件都有各自的Log級(jí)別,且各不相同。而Mybatis統(tǒng)一提供了trace/ debug/ warn/ error四個(gè)級(jí)別
public interface Log {
boolean isDebugEnabled();
boolean isTraceEnabled();
void error(String s, Throwable e);
void error(String s);
void debug(String s);
void trace(String s);
void warn(String s);
}
- 2)自動(dòng)掃描日志實(shí)現(xiàn),并且第三方日志插件加載優(yōu)先級(jí)如下:
slf4j -> commonsLoging -> Log4J2 -> Log4J -> JdkLog
- 3)日志的使用要優(yōu)雅地嵌入到主體功能中
2.相關(guān)設(shè)計(jì)模式——適配器模式
Adapter Pattern 是作為兩個(gè)不兼容的接口之間的橋梁,將一個(gè)類的接口轉(zhuǎn)換成客戶希望的另外一個(gè)接口。適配器模式使得原本由于接口不兼容而不能一起工作的那些類可以一起工作。

- Target:目標(biāo)角色,期待得到的接口
Adaptee:適配者角色,被適配的接口
Adapter:適配器角色,將源接口轉(zhuǎn)換成目標(biāo)接口
適配器滿足:
1)單一職責(zé)原則:target/ adaptee/ adapter都負(fù)責(zé)單一職責(zé)
2)依賴倒轉(zhuǎn)原則:只針對(duì)接口Log編程
3)開閉原則:日志模塊更換時(shí),只需增加一個(gè)針對(duì)新日志模塊的Adapter接口
- 適用場景:當(dāng)調(diào)用雙方都不容易修改的時(shí)候,為了復(fù)用現(xiàn)有組件可以使用適配器模式,在系統(tǒng)中接入第三方組件的時(shí)候經(jīng)常被用到。
- 注意:如果系統(tǒng)中存在過多的適配器,會(huì)增加系統(tǒng)的復(fù)雜性,設(shè)計(jì)人員應(yīng)考慮對(duì)系統(tǒng)進(jìn)行重構(gòu)
3 前兩個(gè)需求的解決
-
日志模塊提供了針對(duì)不同第三方日志組件的Adapter,以實(shí)現(xiàn)統(tǒng)一接口
- LogFactory自動(dòng)掃描日志實(shí)現(xiàn),并且定義了第三方日志插件加載優(yōu)先級(jí)
//自動(dòng)掃描日志實(shí)現(xiàn),并且第三方日志插件加載優(yōu)先級(jí)如下
//slf4J → commonsLoging → Log4J2 → Log4J → JdkLog
static {
tryImplementation(LogFactory::useSlf4jLogging);
tryImplementation(LogFactory::useCommonsLogging);
tryImplementation(LogFactory::useLog4J2Logging);
tryImplementation(LogFactory::useLog4JLogging);
tryImplementation(LogFactory::useJdkLogging);
tryImplementation(LogFactory::useNoLogging);
}

4.相關(guān)設(shè)計(jì)模式——代理模式
- 定義:給目標(biāo)對(duì)象提供一個(gè)代理對(duì)象,并由代理對(duì)象控制對(duì)目標(biāo)對(duì)象的引用
- 目的:
1)通過引入代理對(duì)象的方式來間接訪問目標(biāo)對(duì)象,防止直接訪問目標(biāo)對(duì)象給系統(tǒng)帶來的不必要的復(fù)雜性
2)通過代理對(duì)象對(duì)原有業(yè)務(wù)增強(qiáng) - 代理模式的有點(diǎn)
1)作為中介解耦客戶端和真實(shí)對(duì)象,保護(hù)真實(shí)對(duì)象安全
2)防止直接訪問目標(biāo)對(duì)象給系統(tǒng)帶來的不必要復(fù)雜性
3)對(duì)業(yè)務(wù)進(jìn)行增強(qiáng),增強(qiáng)點(diǎn)多樣化:前入、后入、異常(AOP)
代理模式類圖:

動(dòng)態(tài)代理:


4.1 動(dòng)態(tài)代理實(shí)例

真實(shí)對(duì)象:
public class WangMeiLi implements Girl {
@Override
public void date() {
System.out.println("王美麗說:跟你約會(huì)好開心啊");
//this.watchMovie();
}
@Override
public void watchMovie() {
System.out.println("王美麗說:這個(gè)電影我不喜歡看");
}
}
代理對(duì)象:
public class WangMeiLiProxy implements InvocationHandler {
private Girl girl;
public WangMeiLiProxy(Girl girl) {
super();
this.girl = girl;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
doSomeThingBefore();
Object ret = method.invoke(girl, args);
doSomeThingEnd();
return ret;
}
private void doSomeThingBefore(){
System.out.println("王美麗的父母說:我得先調(diào)查下這個(gè)男孩子的背景!");
}
private void doSomeThingEnd(){
System.out.println("王美麗的父母說:他有沒有對(duì)你動(dòng)手動(dòng)腳???");
}
public Object getProxyInstance(){
return Proxy.newProxyInstance(girl.getClass().getClassLoader(), girl.getClass().getInterfaces(), this);
}
}
測試代碼:
public class Lison {
public static void main(String[] args) {
//隔壁有個(gè)女孩,叫王美麗
Girl girl = new WangMeiLi();
//他有個(gè)龐大的家庭,想要跟她約會(huì)必須征得她家里人的同意
WangMeiLiProxy family = new WangMeiLiProxy(girl);
//有一次我去約王美麗,碰到了她的媽媽,我征得了她媽媽的同意
Girl mother = (Girl) family.getProxyInstance();
//通過她的媽媽這個(gè)代理才能與王美麗約會(huì)
mother.date();
//華麗分割線
System.out.println("-----------------------------------");
//通過她的媽媽這個(gè)代理才能與王美麗看電影
mother.watchMovie();
}
}
結(jié)果:
王美麗的父母說:我得先調(diào)查下這個(gè)男孩子的背景!
王美麗說:跟你約會(huì)好開心啊
王美麗的父母說:他有沒有對(duì)你動(dòng)手動(dòng)腳???
-----------------------------------
王美麗的父母說:我得先調(diào)查下這個(gè)男孩子的背景!
王美麗說:這個(gè)電影我不喜歡看
王美麗的父母說:他有沒有對(duì)你動(dòng)手動(dòng)腳啊?
5.用代理模式解決第三個(gè)需求
核心是jdbc包:

代理對(duì)象的類層次結(jié)構(gòu)圖:

創(chuàng)建代理對(duì)象的地方:
SimpleExecutor.doQuery
prepareStatement
Connection connection = getConnection(statementLog);//獲取connection對(duì)象的動(dòng)態(tài)代理,添加日志能力;
return ConnectionLogger.newInstance(connection, statementLog, queryStack);
6.關(guān)于mybatis源碼調(diào)試說明
mybatis源碼有一個(gè)專門用于測試的目錄:src/test,里面有針對(duì)各個(gè)模塊進(jìn)行測試代碼。
參考
- 1)享學(xué)課堂Lison老師筆記
- 2)Java動(dòng)態(tài)代理
