首先我先解釋一下標題 四大對象是指:executor, statementhandler,parameterhandler,resulthandler對象。(為了方便下面的文章說道四大對象就專指它們)
它們都是sqlsession的底層類實現,也是插件能夠攔截的四大對象。所以這里已經觸及了mybatis的底層,動態代理,反射隨時可以看到,如果沒有第一篇作為基礎,你將十分難以理解它。了解他們的協作,是插件編寫的基礎之一,所以這是十分的重要。
executor在sqlsession中的應用
上篇我們談到了一個問題,一個mapper被執行是通過動態代理來完成的,然后進入到了sqlsession的方法中去。這個并不難理解,但是sqlsession內部是怎么運行的呢?答案四大對象的協作。在sqlsession它還是一個接口,mybatis內部是通過defaultsqlsession這個實現類為我們提供服務的,它比較長,但是我們不需要全部看到,我們只看到很常用的selectlist方法便可以了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package org.apache.ibatis.session.defaults; public class defaultsqlsession implements sqlsession { private configuration configuration; private executor executor; private boolean autocommit; private boolean dirty; ....... @override public <e> list<e> selectlist(string statement, object parameter, rowbounds rowbounds) { try { mappedstatement ms = configuration.getmappedstatement(statement); return executor.query(ms, wrapcollection(parameter), rowbounds, executor.no_result_handler); } catch (exception e) { throw exceptionfactory.wrapexception( "error querying database. cause: " + e, e); } finally { errorcontext.instance().reset(); } } ...... } |
我們可以看到它是通過executor去執行方法來完成查詢的。
初認executor
那么我們對executor就很感興趣,于是我們看看executor是怎么樣的,首先在mybatis中有三種executor:
simpleexecutor -- simple 就是普通的執行器。
reuseexecutor -執行器會重用預處理語句(prepared statements)
batchexecutor --它是批量執行器
這些就是mybatis的三種執行器。你可以通過配置文件的settings里面的元素defaultexecutortype,配置它,默認是采用simpleexecutor如果你在spring運用它,那么你可以這么配置它:
1
2
3
4
5
|
<bean id= "sqlsessiontemplatebatch" class = "org.mybatis.spring.sqlsessiontemplate" > <constructor-arg index= "0" ref= "sqlsessionfactory" /> <!--更新采用批量的executor --> <constructor-arg index= "1" value= "batch" /> </bean> |
這樣,它便是一個批量的執行器。mybatis的三個executor都有一個共同的父類——baseexecutor。
executor初始化
首先我們先了解一下mybatis是怎么樣生成executor的。我們看到生成executor的地方(org.apache.ibatis.session.configuration):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public executor newexecutor(transaction transaction, executortype executortype) { executortype = executortype == null ? defaultexecutortype : executortype; executortype = executortype == null ? executortype.simple : executortype; executor executor; if (executortype.batch == executortype) { executor = new batchexecutor( this , transaction); } else if (executortype.reuse == executortype) { executor = new reuseexecutor( this , transaction); } else { executor = new simpleexecutor( this , transaction); } if (cacheenabled) { executor = new cachingexecutor(executor); } executor = (executor) interceptorchain.pluginall(executor); return executor; } |
這里大部分都很好理解,但是有個地方就好不好理解,它就是:
executor = (executor) interceptorchain.pluginall(executor);
這是一段非常重要的代碼,它是采用責任鏈模式,來產生代理對象。我們需要再深入理解它,打開它具體的pluginall方法:
1
2
3
4
5
6
|
public object pluginall(object target) { for (interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; } |
我們這里先介紹一下這段代碼:
interceptor它是mybatis攔截器必須要實現的接口,換句話說,這個遍歷就是遍歷mybatis的攔截器。
然后調用plugin方法,這個方法是為了生成代理對象(占位)的。
于是可以想象我們的插件的代理對象將會是一層層的嵌套,所以當中任何一個插件(interceptor)都有機會攔截這個真是的服務對象(executor),則便是責任鏈模式,我們完全可以提供插件(interceptor),進入到代理對象的invoke方法里面,來改變executor的行為和方法。
但是我要在這里強調,當你使用插件的時候,你將改變mybatis的executor內容實現,你必須慎重的使用它。
如果要我們自己編寫動態的代理,那么工作量可不小,好在mybatis為我們提供了plugin.java類來完成我們所需要的功能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
public class plugin implements invocationhandler { private object target; private interceptor interceptor; private map< class <?>, set<method>> signaturemap; private plugin(object target, interceptor interceptor, map< class <?>, set<method>> signaturemap) { this .target = target; this .interceptor = interceptor; this .signaturemap = signaturemap; } public static object wrap(object target, interceptor interceptor) { map< class <?>, set<method>> signaturemap = getsignaturemap(interceptor); class <?> type = target.getclass(); class <?>[] interfaces = getallinterfaces(type, signaturemap); if (interfaces.length > 0 ) { return proxy.newproxyinstance( type.getclassloader(), interfaces, new plugin(target, interceptor, signaturemap)); } return target; } @override public object invoke(object proxy, method method, object[] args) throws throwable { try { set<method> methods = signaturemap.get(method.getdeclaringclass()); if (methods != null && methods.contains(method)) { return interceptor.intercept( new invocation(target, method, args)); } return method.invoke(target, args); } catch (exception e) { throw exceptionutil.unwrapthrowable(e); } } ...... } |
這里有一個wrap方法:它會為我們生成代理對象。一旦我們的插件和它綁定,那么我們可以想到就會進入invoke方法里面。
invoke方法:很簡單,它運行首先通過class和method的過濾,看看是否需要攔截這個方法,如果被攔截,那么它就運行interceptor的intercept方法。所以當我們配置了簽名,就能夠攔截我們的方法。
我們先討論那么多,我們知道后面講插件的時候我們還會提及它,這里我們知道它會根據插件的個數生成一層層的代理對象就可以了。
executor的執行
executor的執行是依賴于statement對象來操作的,讓我們以simpleexecutor的doquery方法為例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public class simpleexecutor extends baseexecutor { ...... @override public <e> list<e> doquery(mappedstatement ms, object parameter, rowbounds rowbounds, resulthandler resulthandler, boundsql boundsql) throws sqlexception { statement stmt = null ; try { configuration configuration = ms.getconfiguration(); statementhandler handler = configuration.newstatementhandler(wrapper, ms, parameter, rowbounds, resulthandler, boundsql); stmt = preparestatement(handler, ms.getstatementlog()); return handler.<e>query(stmt, resulthandler); } finally { closestatement(stmt); } } ...... private statement preparestatement(statementhandler handler, log statementlog) throws sqlexception { statement stmt; connection connection = getconnection(statementlog); stmt = handler.prepare(connection); handler.parameterize(stmt); return stmt; } } |
很顯然這里調度的是一個查詢方法
首先它先生成statementhandler對象。
通過preparestatement方法調用prepare方法初始化參數。
然后使用parameterize方法設置參數到運行環境。
然后便通過handler.<e>query(stmt, resulthandler);方法來完成結果組裝。
于是我們的焦點就集中在了statementhandler對象上,下章我們將談及它。
總結
以上所述是小編給大家介紹的mybatis中sqlsession下的四大對象之執行器(executor),希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對服務器之家網站的支持!
原文鏈接:https://blog.csdn.net/ykzhen2015/article/details/50315027