mybatis $和#源代碼分析

JDBC中,主要使用兩種語句,一種是支持參數化和預編譯的PreparedStatement,支持原生sql,支持設置占位符,參數化輸入的參數,防止sql注入攻擊,在mybatis的mapper配置文件中,我們通過使用#和$告訴mybatis我們需要對參數進行怎樣的設置。sql注入指的是利用現有應用程序將惡意的sql命令提交存在安全漏洞。例如在提交表單時加入or拼接語句使其永遠成立。對比JDBC執行流程connection->statement->result,在mybatis中由SqlSession提供給用戶操作的API,Excutor具體執行數據庫的操作。

SqlSession接口主要的實現類有:

先看DefaultSqlSession:

selectOne的方法最終轉化成了selectList方法

最終執行的是箭頭指的selectList方法,該方法中含有變量:MappedSatement和Excutor,MappedSatement是加載mapper.xml時匹配的namespace+id

StrictMap是Configuration類中的一個靜態內部類,繼承了HashMap,看一下selectList方法調用的wrapCollection方法

這就是為什么我們在mapper.xml中foreach用list或array遍歷,再看Excutor執行的query方法,Excutor接口實現類

BaseExcutor的query方法:

由上可知若設置清除緩存,首先會清除緩存,首先會根據CacheKey查找緩存,查找結果為空,則從數據庫查

?

doQuery:

可見Executor委托給StatementHandler執行查詢,在此之前有一個預編譯的過程(prepareStatement方法),StatementHandler接口的實現類:CallableStatementHandler,PreparedStatementHandler,SimpleStatementHandler對應JDBC中的CallableStatement,PreparedStatement和Statement,分別的執行方法:

看之前提到的prepareStatement方法

handler會對statement參數化設置,PreparedStatementHandler中:

由parameterhandler執行參數設置,上面是簡單分析的查詢流程,回頭說$和#,我們知道,使用$時statement執行的是拼接操作,#的時候statement用的是占位符 ?,這是mybatis解析的時候造成的,根據測試例子:

SqlSessionFactoryBuilder的build方法:

XMLConfigBuilder負責解析總配置文件,其中方法有:

返回值都為XNode節點類型,看mapperElement:

package掃描包,resource和class掃描指定類和mapper.xml,XMLMapperBuilder:

主要看這兩個方法,根據節點建立statement:

由上可以看出XMLStatementBuilder解析statement,也就是mapper.xml中的一個個statement,快成功了。。。

parseStatementNode解析一條記錄中的各個屬性,例如resultType,parameterType,useCache等等。。。該方法代碼過長,其主要在:

兩處,一個是SqlSource,一個是addMappedStatement,解析的屬性值都對應到MappedStatement對象中

在MappedStatement對象中除這些外,還有個屬性SqlSource,可見該對象決定sql語句的解析

只有一個抽象方法getBoundSql,SqlSource是如何獲取的呢,這就用到了上面prepareStatement方法中的LanguageDriver的createSqlSource方法,繼續跟進:

解析sql語句之前會先解析selectKey和include節點,LanguageDriver的實現類有XMLLanguageDriver和RawLanguageDriver

可以知道createSqlSource方法只在XMLLanguageDriver實現

委托給了XMLScriptBuilder的parseScriptNode方法:

 1 public SqlSource parseScriptNode() {
 2     List<SqlNode> contents = parseDynamicTags(context);
 3     MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
 4     SqlSource sqlSource = null;
 5     if (isDynamic) {
 6       sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
 7     } else {
 8       sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
 9     }
10     return sqlSource;
11   }

根據isDynamic標志確定sqlSource類型,parseDynamicTags方法:

 1  private List<SqlNode> parseDynamicTags(XNode node) {
 2     List<SqlNode> contents = new ArrayList<SqlNode>();
 3     NodeList children = node.getNode().getChildNodes();
 4     for (int i = 0; i < children.getLength(); i++) {
 5       XNode child = node.newXNode(children.item(i));
 6       if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
 7         String data = child.getStringBody("");
 8         TextSqlNode textSqlNode = new TextSqlNode(data);
 9         if (textSqlNode.isDynamic()) {
10           contents.add(textSqlNode);
11           isDynamic = true;
12         } else {
13           contents.add(new StaticTextSqlNode(data));
14         }
15       } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
16         String nodeName = child.getNode().getNodeName();
17         NodeHandler handler = nodeHandlers.get(nodeName);
18         if (handler == null) {
19           throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
20         }
21         handler.handleNode(child, contents);
22         isDynamic = true;
23       }
24     }
25     return contents;
26   }

返回值是一個list,過程:

?遍歷各個子節點

(1) 如果節點類型是文本或者CDATA,構造一個TextSqlNode或StaticTextSqlNode

(2) 如果節點類型是元素,說明該節點是個動態sql,然后會使用NodeHandler處理各個類型的子節點。這里的NodeHandler是XMLScriptBuilder的一個內部接口,其實現類包括TrimHandler、WhereHandler、SetHandler、IfHandler、ChooseHandler等。看類名也就明白了這個Handler的作用,比如我們分析的trim節點,對應的是TrimHandler;if節點,對應的是IfHandler...,TextSqlNode的isDynamic方法:

1   public boolean isDynamic() {
2     DynamicCheckerTokenParser checker = new DynamicCheckerTokenParser();
3     GenericTokenParser parser = createParser(checker);//建立GenericTokenParser
4     parser.parse(text);//GenericTokenParser解析text
5     return checker.isDynamic();
6   }

createParser方法:

?1 private GenericTokenParser createParser(TokenHandler handler) { 2 return new GenericTokenParser("${", "}", handler); 3 }?

構造方法:

1   public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {
2     this.openToken = openToken;
3     this.closeToken = closeToken;
4     this.handler = handler;
5   }

根據是否Dynamic,TokenHandler的主要實現類有:DynamicCheckerTokenParser和ParameterMappingTokenHandler,VariableTokenHandler

?1 private GenericTokenParser createParser(TokenHandler handler) { 2 return new GenericTokenParser("${", "}", handler); 3 }?//$的處理方式

DynamicCheckerTokenParser實現的handleToken方法

1  public String handleToken(String content) {
2       this.isDynamic = true;
3       return null;
4     }

ParameterMappingTokenHandler實現的handleToken方法:

1  public String handleToken(String content) {//#的處理方式,返回占位符?
2       parameterMappings.add(buildParameterMapping(content));
3       return "?";
4     }

#的方式大概就是這樣,再看$,$使用的是DynamicCheckerTokenParser,這時候再看返回的DynamicSqlSource,其實現SqlSource接口的getBoundSql方法:

 1 public BoundSql getBoundSql(Object parameterObject) {
 2     DynamicContext context = new DynamicContext(configuration, parameterObject);
 3     rootSqlNode.apply(context);//apply方法實際調用的是TextSqlNode的applay方法
 4     SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
 5     Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
 6     SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());//SqlSourceBuilder的parse方法解析
 7     BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
 8     for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
 9       boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
10     }
11     return boundSql;
12   }
TextSqlNode的apply方法:
1 public boolean apply(DynamicContext context) {
2     GenericTokenParser parser = createParser(new BindingTokenParser(context));
3     context.appendSql(parser.parse(text));
4     return true;
5   }

用的是BindingTokenParser的parse方法:

 1  public String parse(String text) {
 2     StringBuilder builder = new StringBuilder();
 3     if (text != null && text.length() > 0) {
 4       char[] src = text.toCharArray();
 5       int offset = 0;
 6       int start = text.indexOf(openToken, offset);
 7       while (start > -1) {
 8         if (start > 0 && src[start - 1] == '\\') {
 9           // the variable is escaped. remove the backslash.
10           builder.append(src, offset, start - 1).append(openToken);
11           offset = start + openToken.length();
12         } else {
13           int end = text.indexOf(closeToken, start);
14           if (end == -1) {
15             builder.append(src, offset, src.length - offset);
16             offset = src.length;
17           } else {
18             builder.append(src, offset, start - offset);
19             offset = start + openToken.length();
20             String content = new String(src, offset, end - offset);
21             builder.append(handler.handleToken(content));//又回到了handleToken方法,此時的handler為BindingTokenParser
22             offset = end + closeToken.length();
23           }
24         }
25         start = text.indexOf(openToken, offset);
26       }
27       if (offset < src.length) {
28         builder.append(src, offset, src.length - offset);
29       }
30     }
31     return builder.toString();
32   }

BindingTokenParser的handleToken:

 1 public String handleToken(String content) {
 2       Object parameter = context.getBindings().get("_parameter");
 3       if (parameter == null) {
 4         context.getBindings().put("value", null);
 5       } else if (SimpleTypeRegistry.isSimpleType(parameter.getClass())) {
 6         context.getBindings().put("value", parameter);//從此處可以看出mapper.xml中$或#中可以用value
 7       }
 8       Object value = OgnlCache.getValue(content, context.getBindings());//此處用了ognl處理
 9       return (value == null ? "" : String.valueOf(value)); // issue #274 return "" instead of "null"
10     }

ognl不太清除,先分析到這



轉載于:https://www.cnblogs.com/miserable-faith/p/7658550.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/454375.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/454375.shtml
英文地址,請注明出處:http://en.pswp.cn/news/454375.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

git 命令詳解和常見問題解決

功能一 提交&#xff1a;1:git init # 初始化&#xff0c;表示即將對當前文件夾進行版本控制2:git status # 查看Git當前狀態&#xff0c;如&#xff1a;那些文件被修改過、那些文件還未提交到版本庫等。3:git add . # 添加當前目錄下所有文件到版本…

辭職日記----記錄31歲的程序員跳槽心態

vcleaner http://topic.csdn.net/u/20080626/23/8f6a8ecc-c072-43ee-bf2d-7ac2286b6805.html http://topic.csdn.net/u/20080704/23/858fc00d-ec14-4db7-93be-34903b7f157a.html 轉載他的離職日記&#xff0c;有許多東西值得我們認真思考&#xff0c;人活著到底為了什么&a…

從Android源碼的角度分析Binder機制

IPC 為了弄懂IPC的來龍去脈&#xff0c;我將從以下三個方面為大家來講解&#xff0c;希望對大家理解IPC會有幫助 什么是IPC IPC是Inter Process Communication的縮寫&#xff0c;其意思就是進程間的通信&#xff0c;也就是兩個進程之間的通信過程。我們都知道在Android系統中&a…

excel vba 調用webbrowser_VBA 公式與函數

一, 在單元格中輸入公式的3種方法:1) 用VBA在單元格中輸入普通公式Sub formula_1() Range("d2") ("B2 * C2") End Sub運行程序后,在D2的單元格內顯示的是公式 B2 * C2 ,并非程序返回值.下文(二)中會介紹另外一種直接返回值的方式想要通過程序一…

內部類可以引用它的包含類的成員嗎?有沒有什么限制?

最近看到一道面試題&#xff1a;內部類可以引用它的包含類的成員嗎&#xff1f;有沒有什么限制&#xff1f; 答案大部分都是這樣子的&#xff1a; 完全可以。如果不是靜態內部類&#xff0c;那沒有什么限制&#xff01; 一個內部類對象可以訪問創建它的外部類對象的成員包括私有…

松下NPM服務器怎么備份系統,松下(Panasonic)-NPM校正amp;CPK完整版教程,一步步帶你成為SMT設備大神!...

馬上注冊&#xff0c;結交更多技術專家&#xff0c;享用更多功能&#xff0c;讓你輕松解決各種三星貼片機問題您需要 登錄 才可以下載或查看&#xff0c;沒有帳號&#xff1f;立即注冊 xa8f80375060fa05b8aebe69ffa21080c.gif (5.26 KB, 下載次數: 3)2019-8-12 00:02 上傳f5aae…

Python 模塊之科學計算 Pandas

目錄 一、Pandas簡介 數據結構 二、Series series 的創建 Series值的獲取 Series的運算 Series缺失值檢測 Series自動對齊 Series及其索引的name屬性 三、DataFrame 創建 Index對象 通過索引值或索引標簽獲取數據 自動化對齊 四、文件操作 文件讀取 數據庫數據…

根據 設備名(br0/eth0/em0)稱獲取 當前機器的IP地址與子網掩碼信息

#!/usr/bin/env python 根據 設備名(br0/eth0/em0)稱獲取 當前機器的IP地址與子網掩碼信息import socket, struct, fcntldef get_ipaddress(ifname eth0):s socket.socket(socket.AF_INET, socket.SOCK_DGRAM)return socket.inet_ntoa(fcntl.ioctl(s.fileno(),0x8915, # SI…

我的程序生涯

本文僅為愛好程序及向往真正之程序員者所作&#xff0c;其余人等可忽略下文。 如今&#xff0c;接觸CS幾近八年&#xff0c;不學無術&#xff0c;所精之物鮮也&#xff0c;以至一事無成。 現回憶吾程序之生涯&#xff0c;以整理繁雜之心緒。 1. 接觸計算機和編程語言 02年始大…

機器學習中qa測試_如何對機器學習做單元測試

作者&#xff1a;Chase Roberts編譯&#xff1a;ronghuaiyang導讀養成良好的單元測試的習慣&#xff0c;真的是受益終身的&#xff0c;特別是機器學習代碼&#xff0c;有些bug真不是看看就能看出來的。在過去的一年里&#xff0c;我把大部分的工作時間都花在了深度學習研究和實…

項目寶提供的服務器,開源WebSocket服務器項目寶貝魚CshBBrain V4.0.1 和 V2.0.2發布

開源WebSocket服務器項目寶貝魚CshBBrain V4.0.1 和 V2.0.2發布更新的功能列表如下&#xff1a;1.解決開啟廣播消息開關時&#xff0c;不能同時接入2個客戶端的重大缺陷。2.對廣播消息做了重大優化&#xff0c;從以前一個線程發送廣播消息進化到使用工作線程池中的線程并行的發…

c# 無損高質量壓縮圖片代碼

/// 無損壓縮圖片 /// <param name"sFile">原圖片</param> /// <param name"dFile">壓縮后保存位置</param> /// <param name"dHeight">高度</param> /// <param name"dWidth"…

一個從文本文件里“查找并替換”的功能

12345678910111213141516171819202122232425# -*- coding: UTF-8 -*-file input("請輸入文件路徑:") word1 input("請輸入要替換的詞:") word2 input("請輸入新的詞&#xff1a;") fopen(file,"r") AAAf.read() count 0 def BBB()…

機器學習算法之 KNN

K近鄰法(k-nearst neighbors,KNN)是一種很基本的機器學習方法了&#xff0c;在我們平常的生活中也會不自主的應用。比如&#xff0c;我們判斷一個人的人品&#xff0c;只需要觀察他來往最密切的幾個人的人品好壞就可以得出了。這里就運用了KNN的思想。KNN方法既可以做分類&…

安裝云端服務器操作系統,安裝云端服務器操作系統

安裝云端服務器操作系統 內容精選換一換SAP云服務器規格在申請SAP ECS之前&#xff0c;請參考SAP標準Sizing方法進行SAPS值評估&#xff0c;并根據Sizing結果申請云端ECS服務器資源&#xff0c;詳細信息請參考SAP Quick Sizer。SAP 各組件最低硬盤空間、RAM&#xff0c;以及軟件…

python 進度條_六種酷炫Python運行進度條

轉自&#xff1a;一行數據閱讀文本大概需要 3 分鐘你的代碼進度還剩多少&#xff1f;今天給大家介紹下目前6種比較常用的進度條&#xff0c;讓大家都能直觀地看到腳本運行最新的進展情況。1.普通進度條2.帶時間進度條3.tpdm進度條4.progress進度條5.alive_progress進度條6.可視…

js 獲取多少天前

getBeforeDate: function(day, str) { var now new Date().getTime(); //獲取毫秒數 var before new Date(now - ((day > 0 && day ? day : 0) * 86400 * 1000)); var year before.getFullYear(); var month before.getMonth()1; var date before.getDate(); …

程序員的基本素質

給所有立志成為程序員的朋友 以及 自勉之&#xff01; 程序員基本素質&#xff1a; 作一個真正合格的程序員&#xff0c;或者說就是可以真正合格完成一些代碼工作的程序員&#xff0c;應該具有的素質。 1&#xff1a;團隊精神和協作能力 把它作為基本素質&#xff0c;并…

權限之淺理解

白馬過隙&#xff0c;在感嘆時光流逝的同時不得不承認在學習中隨著知識面的不斷擴展所接受的東西也越來越多&#xff0c;尤其是那些外形比較容易混淆的命令&#xff0c;著實讓作為新手的吃了很多苦頭&#xff0c;趁著學習緊張之時偷個懶整理這周易混淆的命令&#xff1a; chgrp…

機器學習算法之生成樹

一、什么是決策樹&#xff1f; 決策樹&#xff08;Decision Tree&#xff09;是一種基本的分類和回歸的方法。 分類決策樹模型是一種描述對實例進行分類的樹形結構。決策樹由結點&#xff08;node&#xff09;和有向邊&#xff08;directed edge&#xff09;組成。結點有兩種…