电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術(shù)文章
文章詳情頁

分析mybatis運行原理

瀏覽:49日期:2023-10-19 08:56:04
目錄一、Mybatis基本認識1.1、動態(tài)代理1.2、反射二、Configuration對象作用三、映射器結(jié)構(gòu)四、sqlsession執(zhí)行流程(源碼跟蹤)4.1、Executor4.2、StatementHandler4.3、結(jié)果處理器(ResultSetHandler)4.4、總結(jié)一、Mybatis基本認識1.1、動態(tài)代理 之前我們知道Mapper僅僅是一個接口,而不是一個邏輯實現(xiàn)類。但是在Java中接口是無法執(zhí)行邏輯的。這里Mybatis就是通過動態(tài)代理實現(xiàn)的。關(guān)于動態(tài)代理我們常用的有Jdk動態(tài)代理和cglib動態(tài)代理。兩種卻別這里不做贅述。關(guān)于CGLIB代理在框架中使用的比較多。 關(guān)于動態(tài)代理就是所有的請求有一個入口,由這個入口進行分發(fā)。在開發(fā)領(lǐng)域的一個用途就是【負載均衡】 關(guān)于Mybatis的動態(tài)代理是使用了兩種的結(jié)合。 下面看看JDK和cglib兩種實現(xiàn)

JDK實現(xiàn)首先我們需要提供一個接口 , 這個接口是對我們程序員的一個抽象。 擁有編碼和改BUG的本領(lǐng)

public interface Developer { /** * 編碼 */ void code(); /** * 解決問題 */ void debug();}

關(guān)于這兩種本領(lǐng)每個人處理方式不同。這里我們需要一個具體的實例對象

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)用方式是通過java提供的new 機制創(chuàng)造一個JavaDeveloper對象出來。而通過動態(tài)代理是通過java.lang.reflect.Proxy對象創(chuàng)建對象調(diào)用實際方法的。

通過newProxyInstance方法獲取接口對象的。而這個方法需要三個參數(shù)

ClassLoader loader : 通過實際接口實例對象獲取ClassLoader Class<?>[] interfaces : 我們抽象的接口 InvocationHandler h : 對我們接口對象方法的調(diào)用。在調(diào)用節(jié)點我們可以進行我們的業(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('我是一個特殊的人,code之前先分析問題');return method.invoke(jDeveloper, params); } if (method.getName().equals('debug')) {System.out.println('我沒有bug'); } return null;});developer.code();developer.debug();

CGLIB動態(tài)代理

cglib動態(tài)代理優(yōu)點在于他不需要我們提前準備接口。他代理的實際的對象。這對于我們開發(fā)來說就很方便了。

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'); }}

下面我們只需要實現(xiàn)cglib提供的MethodInterceptor接口,在初始化設(shè)置cglib的時候加載這個實例化對象就可以了

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; }}

下面我們就來初始化設(shè)置cglib

public static void main(String[] args) { //代理類class文件存入本地磁盤方便我們反編譯查看源代碼 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, '/root/code'); //通過CGLIB動態(tài)代理獲取代理對象過程 Enhancer enhancer = new Enhancer(); //設(shè)置enhancer對象的父類 enhancer.setSuperclass(HelloService.class); // 設(shè)置enhancer的回調(diào)對象 enhancer.setCallback(new MyMethodInterceptor()); //創(chuàng)建代理對象 HelloService helloService = (HelloService) enhancer.create(); //通過代理對象調(diào)用目標方法 helloService.sayHello();}

仔細看看cglib和spring的aop特別像。針對切點進行切面攔截控制。

總結(jié):

通過對比兩種動態(tài)代理我們很容易發(fā)現(xiàn),mybatis就是通過JDK代理實現(xiàn)Mapper調(diào)用的。我們Mapper接口實現(xiàn)通過代理到xml中對應(yīng)的sql執(zhí)行邏輯

1.2、反射 相信有一定經(jīng)驗的Java工程師都對反射或多或少有一定了解。其實從思想上看不慣哪種語言都是有反射的機制的。 通過反射我們就擺脫了對象的限制我們調(diào)用方法不再需要通過對象調(diào)用了??梢酝ㄟ^Class對象獲取方法對象。從而通過invoke方法進行方法的調(diào)用了。二、Configuration對象作用

Configuration對象存儲了所有Mybatis的配置。主要初始化一下參數(shù)

properties settings typeAliases typeHandler ObjectFactory plugins environment DatabaseIdProvider Mapper映射器三、映射器結(jié)構(gòu)

分析mybatis運行原理

BoundSql提供三個主要的屬性 parameterMappings 、parameterObject、sql parameterObject參數(shù)本身。我們可以傳遞java基本類型、POJO、Map或者@Param標注的參數(shù)。 當我們傳遞的是java基本類型mybatis會轉(zhuǎn)換成對應(yīng)的包裝對象 int -> Integer 如果我們傳遞POJO、Map。就是對象本身 我們傳遞多個參數(shù)且沒有@Param指定變量名則parameterObject 類似 {'1':p1,'2':p2,'param1':p1,'param2':p2} 我們傳遞多個參數(shù)且@Param指定變量名 則parameterObject類似 {'key1':p1,'key2':p2,'param1':p1,'param2':p2} parameterMapping 是記錄屬性、名稱、表達式、javaType,jdbcType、typeHandler這些信息 sql 屬性就是我們映射器中的一條sql. 正常我們在常見中對sql進行校驗。正常不需要修改sql。四、sqlsession執(zhí)行流程(源碼跟蹤)

首先我們看看我們平時開發(fā)的Mapper接口是如何動態(tài)代理的。這就需要提到MapperProxyFactory這個類了。該類中的newInstance方法

protected T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); }

通過上滿代碼及上述對jdk動態(tài)代理的表述。我們可以知道m(xù)apperProxy是我們代理的重點。MapperProxy是InvocationHandler的實現(xiàn)類。他重寫的invoke方法就是代理對象執(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();}

通過源碼發(fā)現(xiàn)。invoke內(nèi)部首先判斷對象是否是類 。 通過打斷點發(fā)現(xiàn)最終會走到cacheMapperMethod這個方法去創(chuàng)建MapperMethod對象。繼續(xù)查看MapperMethod中execute方法我們可以了解到內(nèi)部實現(xiàn)其實是一個命令行模式開發(fā)。通過判斷命令從而執(zhí)行不同的語句。判斷到具體執(zhí)行語句然后將參數(shù)傳遞給sqlsession進行sql調(diào)用并獲取結(jié)果。到了sqlsession就和正常jdbc開發(fā)sql進行關(guān)聯(lián)了。sqlsession中Executor、StatementHandler、ParameterHandler、Resulthandler四大天王

4.1、Executor

顧名思義他就是一個執(zhí)行器。將java提供的sql提交到數(shù)據(jù)庫。Mybatis提供了三種執(zhí)行器。

Configuration.class中newExecutor源碼

分析mybatis運行原理

根據(jù)uml我們不難看出mybatis中提供了三類執(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); // 返回默認的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(); } }

通過上述源碼我們知道在sqlsession獲取一個數(shù)據(jù)庫session對象時我們或根據(jù)我們的settings配置加載一個Executor對象。在settings中配置也很簡單

<settings><!--取值范圍 SIMPLE, REUSE, BATCH --><setting name='defaultExecutorType' value='SIMPLE'/></settings>

我們也可以通過java代碼設(shè)置

factory.openSession(ExecutorType.BATCH);4.2、StatementHandler

顧名思義,StatementHandler就是專門處理數(shù)據(jù)庫回話的。這個對象的創(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這個class

分析mybatis運行原理

關(guān)于StatementHandler和RoutingStatementHandler之間的關(guān)系我們通過源碼可以看出這里和Executor一樣都是適配器模式。采用這種模式的好處是方便我們對這些對象進行代理。這里讀者可以猜測一下是使用了哪種動態(tài)代理。給點提示 這里使用了接口哦

分析mybatis運行原理分析mybatis運行原理

在查看BaseStatementHandler結(jié)構(gòu)我們會發(fā)現(xiàn)和Executor一模一樣。同樣的Mybatis在構(gòu)造RoutingStatementHandler的時候會根據(jù)setting中配置來加載不同的具體子類。這些子類都是繼承了BaseStatementHandler.

前一節(jié)我們跟蹤了Executor。 我們知道Mybatis默認的是SimpleExecutor。 StatementHandler我們跟蹤了Mybaits默認的是PrePareStatementHandler。在SimpleExecutor執(zhí)行查詢的源碼如下

分析mybatis運行原理分析mybatis運行原理

我們發(fā)現(xiàn)在executor查詢錢會先讓statementHandler構(gòu)建一個Statement對象。最終就是StatementHandler中prepare方法。這個方法在抽象類BaseStatmentHandler中已經(jīng)封裝好了。

分析mybatis運行原理

這個方法的邏輯是初始化statement和設(shè)置連接超時等一些輔助作用

然后就是設(shè)置一些參數(shù)等設(shè)置。最后就走到了執(zhí)行器executor的doquery

分析mybatis運行原理

PrepareStatement在我們jdbc開發(fā)時是常見的一個類 。 這個方法執(zhí)行execute前我們需要設(shè)置sql語句,設(shè)置參數(shù)進行編譯。這一系列步驟就是剛才我們說的流程也是PrepareStatementHandler.prepareStatement幫我們做的事情。那么剩下的我們也很容易想到就是我們對數(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); }

這個方法我們可以導(dǎo)出來是結(jié)果xml中標簽配置對結(jié)果的一個封裝。

4.4、總結(jié)

SqlSession在一個查詢開啟的時候會先通過CacheExecutor查詢緩存。擊穿緩存后會通過BaseExector子類的SimpleExecutor創(chuàng)建StatementHandler。PrepareStatementHandler會基于PrepareStament執(zhí)行數(shù)據(jù)庫操作。并針對返回結(jié)果通過ResultSetHandler返回結(jié)果數(shù)據(jù)

分析mybatis運行原理

以上就是分析mybatis運行原理的詳細內(nèi)容,更多關(guān)于mybatis運行原理的資料請關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

相關(guān)文章:
主站蜘蛛池模板: 连续密炼机_双转子连续密炼机_连续式密炼机-南京永睿机械制造有限公司 | 伟秀电气有限公司-10kv高低压开关柜-高低压配电柜-中置柜-充气柜-欧式箱变-高压真空断路器厂家 | 成都中天自动化控制技术有限公司 | 高压包-点火器-高压发生器-点火变压器-江苏天网 | 仓储货架_南京货架_钢制托盘_仓储笼_隔离网_环球零件盒_诺力液压车_货架-南京一品仓储设备制造公司 | 全自动过滤器_反冲洗过滤器_自清洗过滤器_量子除垢环_量子环除垢_量子除垢 - 安士睿(北京)过滤设备有限公司 | 水冷式工业冷水机组_风冷式工业冷水机_水冷螺杆冷冻机组-深圳市普威机械设备有限公司 | 探伤仪,漆膜厚度测试仪,轮胎花纹深度尺厂家-淄博创宇电子 | 新疆系统集成_新疆系统集成公司_系统集成项目-新疆利成科技 | 碳刷_刷握_集电环_恒压簧_电刷厂家-上海丹臻机电科技有限公司 | 济南展厅设计施工_数字化展厅策划设计施工公司_山东锐尚文化传播有限公司 | 智能垃圾箱|垃圾房|垃圾分类亭|垃圾分类箱专业生产厂家定做-宿迁市传宇环保设备有限公司 | 超声波破碎仪-均质乳化机(供应杭州,上海,北京,广州,深圳,成都等地)-上海沪析实业有限公司 | 无缝方管|无缝矩形管|无缝方矩管|无锡方管厂家 | 电销卡_稳定企业大语音卡-归属地可选-世纪通信 | 电力测功机,电涡流测功机,磁粉制动器,南通远辰曳引机测试台 | 安徽泰科检测科技有限公司【官方网站】| 英国公司注册-新加坡公司注册-香港公司开户-离岸公司账户-杭州商标注册-杭州优创企业 | 517瓜水果特产网|一个专注特产好物的网站 | 湿地保护| 政府回应:200块在义乌小巷能买到爱情吗?——揭秘打工族省钱约会的生存智慧 | 智能交通网_智能交通系统_ITS_交通监控_卫星导航_智能交通行业 | 首页|专注深圳注册公司,代理记账报税,注册商标代理,工商变更,企业400电话等企业一站式服务-慧用心 | 食品级焦亚硫酸钠_工业级焦亚硫酸钠_焦亚硫酸钠-潍坊邦华化工有限公司 | 阀门智能定位器_电液动执行器_气动执行机构-赫尔法流体技术(北京)有限公司 | 袋式过滤器,自清洗过滤器,保安过滤器,篮式过滤器,气体过滤器,全自动过滤器,反冲洗过滤器,管道过滤器,无锡驰业环保科技有限公司 | 济南品牌包装设计公司_济南VI标志设计公司_山东锐尚文化传播 | 泰国试管婴儿_泰国第三代试管婴儿费用|成功率|医院—新生代海外医疗 | 压力控制器,差压控制器,温度控制器,防爆压力控制器,防爆温度控制器,防爆差压控制器-常州天利智能控制股份有限公司 | 上海电子秤厂家,电子秤厂家价格,上海吊秤厂家,吊秤供应价格-上海佳宜电子科技有限公司 | 杭州顺源过滤机械有限公司官网-压滤机_板框压滤机_厢式隔膜压滤机厂家 | 工业插头-工业插头插座【厂家】-温州罗曼电气| 贵州科比特-防雷公司厂家提供贵州防雷工程,防雷检测,防雷接地,防雷设备价格,防雷产品报价服务-贵州防雷检测公司 | 东莞市超赞电子科技有限公司 全系列直插/贴片铝电解电容,电解电容,电容器 | 润滑油加盟_润滑油厂家_润滑油品牌-深圳市沃丹润滑科技有限公司 琉璃瓦-琉璃瓦厂家-安徽盛阳新型建材科技有限公司 | 信阳市建筑勘察设计研究院有限公司 | 宏源科技-房地产售楼系统|线上开盘系统|售楼管理系统|线上开盘软件 | 开云(中国)Kaiyun·官方网站 - 登录入口 | 珠海白蚁防治_珠海灭鼠_珠海杀虫灭鼠_珠海灭蟑螂_珠海酒店消杀_珠海工厂杀虫灭鼠_立净虫控防治服务有限公司 | 诺冠气动元件,诺冠电磁阀,海隆防爆阀,norgren气缸-山东锦隆自动化科技有限公司 | 无机纤维喷涂棉-喷涂棉施工工程-山东华泉建筑工程有限公司▲ |