分析mybatis運(yùn)行原理
JDK實(shí)現(xiàn)首先我們需要提供一個(gè)接口 , 這個(gè)接口是對(duì)我們程序員的一個(gè)抽象。 擁有編碼和改BUG的本領(lǐng)
public interface Developer { /** * 編碼 */ void code(); /** * 解決問(wèn)題 */ void debug();}
關(guān)于這兩種本領(lǐng)每個(gè)人處理方式不同。這里我們需要一個(gè)具體的實(shí)例對(duì)象
public class JavaDeveloper implements Developer { @Override public void code() {System.out.println('java code'); } @Override public void debug() {System.out.println('java debug'); }}
我們傳統(tǒng)的調(diào)用方式是通過(guò)java提供的new 機(jī)制創(chuàng)造一個(gè)JavaDeveloper對(duì)象出來(lái)。而通過(guò)動(dòng)態(tài)代理是通過(guò)java.lang.reflect.Proxy對(duì)象創(chuàng)建對(duì)象調(diào)用實(shí)際方法的。
通過(guò)newProxyInstance方法獲取接口對(duì)象的。而這個(gè)方法需要三個(gè)參數(shù)
ClassLoader loader : 通過(guò)實(shí)際接口實(shí)例對(duì)象獲取ClassLoader Class<?>[] interfaces : 我們抽象的接口 InvocationHandler h : 對(duì)我們接口對(duì)象方法的調(diào)用。在調(diào)用節(jié)點(diǎn)我們可以進(jìn)行我們的業(yè)務(wù)攔截JavaDeveloper jDeveloper = new JavaDeveloper();Developer developer = (Developer) Proxy.newProxyInstance(jDeveloper.getClass().getClassLoader(), jDeveloper.getClass().getInterfaces(), (proxy, method, params) -> { if (method.getName().equals('code')) {System.out.println('我是一個(gè)特殊的人,code之前先分析問(wèn)題');return method.invoke(jDeveloper, params); } if (method.getName().equals('debug')) {System.out.println('我沒(méi)有bug'); } return null;});developer.code();developer.debug();
CGLIB動(dòng)態(tài)代理
cglib動(dòng)態(tài)代理優(yōu)點(diǎn)在于他不需要我們提前準(zhǔn)備接口。他代理的實(shí)際的對(duì)象。這對(duì)于我們開(kāi)發(fā)來(lái)說(shuō)就很方便了。
public class HelloService { public HelloService() {System.out.println('HelloService構(gòu)造'); } final public String sayHello(String name) {System.out.println('HelloService:sayOthers>>'+name);return null; } public void sayHello() {System.out.println('HelloService:sayHello'); }}
下面我們只需要實(shí)現(xiàn)cglib提供的MethodInterceptor接口,在初始化設(shè)置cglib的時(shí)候加載這個(gè)實(shí)例化對(duì)象就可以了
public class MyMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println('======插入前置通知======');Object object = methodProxy.invokeSuper(o, objects);System.out.println('======插入后者通知======');return object; }}
下面我們就來(lái)初始化設(shè)置cglib
public static void main(String[] args) { //代理類(lèi)class文件存入本地磁盤(pán)方便我們反編譯查看源代碼 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, '/root/code'); //通過(guò)CGLIB動(dòng)態(tài)代理獲取代理對(duì)象過(guò)程 Enhancer enhancer = new Enhancer(); //設(shè)置enhancer對(duì)象的父類(lèi) enhancer.setSuperclass(HelloService.class); // 設(shè)置enhancer的回調(diào)對(duì)象 enhancer.setCallback(new MyMethodInterceptor()); //創(chuàng)建代理對(duì)象 HelloService helloService = (HelloService) enhancer.create(); //通過(guò)代理對(duì)象調(diào)用目標(biāo)方法 helloService.sayHello();}
仔細(xì)看看cglib和spring的aop特別像。針對(duì)切點(diǎn)進(jìn)行切面攔截控制。
總結(jié):
通過(guò)對(duì)比兩種動(dòng)態(tài)代理我們很容易發(fā)現(xiàn),mybatis就是通過(guò)JDK代理實(shí)現(xiàn)Mapper調(diào)用的。我們Mapper接口實(shí)現(xiàn)通過(guò)代理到xml中對(duì)應(yīng)的sql執(zhí)行邏輯
1.2、反射 相信有一定經(jīng)驗(yàn)的Java工程師都對(duì)反射或多或少有一定了解。其實(shí)從思想上看不慣哪種語(yǔ)言都是有反射的機(jī)制的。 通過(guò)反射我們就擺脫了對(duì)象的限制我們調(diào)用方法不再需要通過(guò)對(duì)象調(diào)用了??梢酝ㄟ^(guò)Class對(duì)象獲取方法對(duì)象。從而通過(guò)invoke方法進(jìn)行方法的調(diào)用了。二、Configuration對(duì)象作用Configuration對(duì)象存儲(chǔ)了所有Mybatis的配置。主要初始化一下參數(shù)
properties settings typeAliases typeHandler ObjectFactory plugins environment DatabaseIdProvider Mapper映射器三、映射器結(jié)構(gòu)首先我們看看我們平時(shí)開(kāi)發(fā)的Mapper接口是如何動(dòng)態(tài)代理的。這就需要提到MapperProxyFactory這個(gè)類(lèi)了。該類(lèi)中的newInstance方法
protected T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); }
通過(guò)上滿代碼及上述對(duì)jdk動(dòng)態(tài)代理的表述。我們可以知道m(xù)apperProxy是我們代理的重點(diǎn)。MapperProxy是InvocationHandler的實(shí)現(xiàn)類(lèi)。他重寫(xiě)的invoke方法就是代理對(duì)象執(zhí)行的方法入口。
@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try { if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } else if (isDefaultMethod(method)) { return invokeDefaultMethod(proxy, method, args); }} catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t);}final MapperMethod mapperMethod = cachedMapperMethod(method);return mapperMethod.execute(sqlSession, args);}
private boolean isDefaultMethod(Method method) {return (method.getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC && method.getDeclaringClass().isInterface();}
通過(guò)源碼發(fā)現(xiàn)。invoke內(nèi)部首先判斷對(duì)象是否是類(lèi) 。 通過(guò)打斷點(diǎn)發(fā)現(xiàn)最終會(huì)走到cacheMapperMethod這個(gè)方法去創(chuàng)建MapperMethod對(duì)象。繼續(xù)查看MapperMethod中execute方法我們可以了解到內(nèi)部實(shí)現(xiàn)其實(shí)是一個(gè)命令行模式開(kāi)發(fā)。通過(guò)判斷命令從而執(zhí)行不同的語(yǔ)句。判斷到具體執(zhí)行語(yǔ)句然后將參數(shù)傳遞給sqlsession進(jìn)行sql調(diào)用并獲取結(jié)果。到了sqlsession就和正常jdbc開(kāi)發(fā)sql進(jìn)行關(guān)聯(lián)了。sqlsession中Executor、StatementHandler、ParameterHandler、Resulthandler四大天王
4.1、Executor顧名思義他就是一個(gè)執(zhí)行器。將java提供的sql提交到數(shù)據(jù)庫(kù)。Mybatis提供了三種執(zhí)行器。
Configuration.class中newExecutor源碼
根據(jù)uml我們不難看出mybatis中提供了三類(lèi)執(zhí)行器分別SimpleExecutor、ReuseExecutor、BatchExecutor
public SqlSession openSession() { return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);}private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { // 得到configuration 中的environment final Environment environment = configuration.getEnvironment(); // 得到configuration 中的事務(wù)工廠 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); // 獲取執(zhí)行器 final Executor executor = configuration.newExecutor(tx, execType); // 返回默認(rèn)的SqlSession return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException('Error opening session. Cause: ' + e, e); } finally { ErrorContext.instance().reset(); } }
通過(guò)上述源碼我們知道在sqlsession獲取一個(gè)數(shù)據(jù)庫(kù)session對(duì)象時(shí)我們或根據(jù)我們的settings配置加載一個(gè)Executor對(duì)象。在settings中配置也很簡(jiǎn)單
<settings><!--取值范圍 SIMPLE, REUSE, BATCH --><setting name='defaultExecutorType' value='SIMPLE'/></settings>
我們也可以通過(guò)java代碼設(shè)置
factory.openSession(ExecutorType.BATCH);4.2、StatementHandler
顧名思義,StatementHandler就是專(zhuān)門(mén)處理數(shù)據(jù)庫(kù)回話的。這個(gè)對(duì)象的創(chuàng)建還是在Configuration中管理的。
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); return statementHandler; }
很明顯Mybatis中StatementHandler使用的是RoutingStatementHandler這個(gè)class
關(guān)于StatementHandler和RoutingStatementHandler之間的關(guān)系我們通過(guò)源碼可以看出這里和Executor一樣都是適配器模式。采用這種模式的好處是方便我們對(duì)這些對(duì)象進(jìn)行代理。這里讀者可以猜測(cè)一下是使用了哪種動(dòng)態(tài)代理。給點(diǎn)提示 這里使用了接口哦
在查看BaseStatementHandler結(jié)構(gòu)我們會(huì)發(fā)現(xiàn)和Executor一模一樣。同樣的Mybatis在構(gòu)造RoutingStatementHandler的時(shí)候會(huì)根據(jù)setting中配置來(lái)加載不同的具體子類(lèi)。這些子類(lèi)都是繼承了BaseStatementHandler.
前一節(jié)我們跟蹤了Executor。 我們知道Mybatis默認(rèn)的是SimpleExecutor。 StatementHandler我們跟蹤了Mybaits默認(rèn)的是PrePareStatementHandler。在SimpleExecutor執(zhí)行查詢(xún)的源碼如下
我們發(fā)現(xiàn)在executor查詢(xún)錢(qián)會(huì)先讓statementHandler構(gòu)建一個(gè)Statement對(duì)象。最終就是StatementHandler中prepare方法。這個(gè)方法在抽象類(lèi)BaseStatmentHandler中已經(jīng)封裝好了。
這個(gè)方法的邏輯是初始化statement和設(shè)置連接超時(shí)等一些輔助作用
然后就是設(shè)置一些參數(shù)等設(shè)置。最后就走到了執(zhí)行器executor的doquery
PrepareStatement在我們jdbc開(kāi)發(fā)時(shí)是常見(jiàn)的一個(gè)類(lèi) 。 這個(gè)方法執(zhí)行execute前我們需要設(shè)置sql語(yǔ)句,設(shè)置參數(shù)進(jìn)行編譯。這一系列步驟就是剛才我們說(shuō)的流程也是PrepareStatementHandler.prepareStatement幫我們做的事情。那么剩下的我們也很容易想到就是我們對(duì)數(shù)據(jù)結(jié)果的封裝。正如代碼所示下馬就是resultSetHandler幫我們做事情了。
4.3、結(jié)果處理器(ResultSetHandler)@Override public List<Object> handleResultSets(Statement stmt) throws SQLException { ErrorContext.instance().activity('handling results').object(mappedStatement.getId()); final List<Object> multipleResults = new ArrayList<>(); int resultSetCount = 0; ResultSetWrapper rsw = getFirstResultSet(stmt); List<ResultMap> resultMaps = mappedStatement.getResultMaps(); int resultMapCount = resultMaps.size(); validateResultMapsCount(rsw, resultMapCount); while (rsw != null && resultMapCount > resultSetCount) { ResultMap resultMap = resultMaps.get(resultSetCount); handleResultSet(rsw, resultMap, multipleResults, null); rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount++; } String[] resultSets = mappedStatement.getResultSets(); if (resultSets != null) { while (rsw != null && resultSetCount < resultSets.length) {ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);if (parentMapping != null) { String nestedResultMapId = parentMapping.getNestedResultMapId(); ResultMap resultMap = configuration.getResultMap(nestedResultMapId); handleResultSet(rsw, resultMap, null, parentMapping);}rsw = getNextResultSet(stmt);cleanUpAfterHandlingResultSet();resultSetCount++; } } return collapseSingleResultList(multipleResults); }
這個(gè)方法我們可以導(dǎo)出來(lái)是結(jié)果xml中標(biāo)簽配置對(duì)結(jié)果的一個(gè)封裝。
4.4、總結(jié)SqlSession在一個(gè)查詢(xún)開(kāi)啟的時(shí)候會(huì)先通過(guò)CacheExecutor查詢(xún)緩存。擊穿緩存后會(huì)通過(guò)BaseExector子類(lèi)的SimpleExecutor創(chuàng)建StatementHandler。PrepareStatementHandler會(huì)基于PrepareStament執(zhí)行數(shù)據(jù)庫(kù)操作。并針對(duì)返回結(jié)果通過(guò)ResultSetHandler返回結(jié)果數(shù)據(jù)
以上就是分析mybatis運(yùn)行原理的詳細(xì)內(nèi)容,更多關(guān)于mybatis運(yùn)行原理的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!
相關(guān)文章:
1. mysql的like模式2. oracle觸發(fā)器介紹3. 詳解Mysql日期格式并聚合統(tǒng)計(jì)示例4. 快速刪除ORACLE重復(fù)記錄5. ACCESS轉(zhuǎn)SQL數(shù)據(jù)庫(kù)相關(guān)的幾個(gè)技能6. Delphi中的Access技巧集7. 啟動(dòng)MYSQL出錯(cuò) Manager of pid-file quit without updating file.8. 通過(guò)Backup Exec實(shí)施Oracle來(lái)災(zāi)難恢復(fù)9. SqlServer服務(wù)中利用觸發(fā)器對(duì)指定賬戶(hù)進(jìn)行登錄ip限制提升安全性操作10. 破解Oracle中國(guó)高層頻繁變動(dòng)之謎
