### 重新認識MappedStatement
每個MappedStatement對應了我們自定義Mapper接口中的一個方法,它保存了開發人員編寫的SQL語句、參數結構、返回值結構、Mybatis對它的處理方式的配置等細節要素,是對一個SQL命令是什么、執行方式的完整定義。可以說,有了它Mybatis就知道如何去調度四大組件順利的完成用戶請求。
MappedStatement保存在Configuration#mappedStatements這個Map類型的對象中,其存儲的key為MappedStatement#id,所以MappedStatement的id是不能重復的,這個id是由Mapper接口的完全限定名和方法名稱拼接而成,這就導致了我們在同一個Mapper中不能出現重載的接口方法。
按照Mybatis的規范,每個Mapper方法也會對應xml中一個select/insert/update/delete標簽,Mybatis為這些標簽設計了一些屬性,允許我們開發人員修改Mybatis的運行方式或行為。大部分情況下,我們不會關注這些屬性,是因為Mybatis為其設計了默認值,方便我們開箱即用。我把MappedStatement類中的主要字段進行了注釋,這些字段都可以找到與之對應的標簽屬性,可參考[《XML映射文件》](https://mybatis.org/mybatis-3/zh/sqlmap-xml.html),代碼貼在了下面,大家先對MappedStatement有個大概的認識。
~~~java
public final class MappedStatement {
/**
* 對應所屬mapper的資源路徑,如我們示例中的CompanyMapper.xml
*/
private String resource;
/**
* mybatis全局的配置對象
*/
private Configuration configuration;
/**
* 當前MappedStatement的唯一識別ID,并且在同一個Configuration中是唯一的
* 它由Mapper類的完全限定名和Mapper方法名稱拼接而成
*/
private String id;
/**
* mybatis每次從數據庫中返回記錄的大小,通過對該值的優化,可以提升查詢效率
*/
private Integer fetchSize;
/**
* 當前MappedStatement執行時,數據庫操作的超時時間
*/
private Integer timeout;
/**
* SQL聲明的類型,決定當前MappedStatement由哪種類型的StatementHandler執行
* StatementType枚舉有:STATEMENT, PREPARED, CALLABLE
* 默認值是:PREPARED
*/
private StatementType statementType;
/**
* 結果集處理類型,決定了結果集游標的移動方式:
* 只能向前移動、雙向移動且對修改敏感、雙向移動對修改不敏感。
*/
private ResultSetType resultSetType;
/**
* 存儲我們定義的經過mybatis初步解析處理的sql語句,由若干sql節點構成,包含一些動態節點,如If條件語句。
* 在生成SqlSource之前,已經把<include></include>標簽的內容轉為了實際的文本對象
*/
private SqlSource sqlSource;
/**
* 二級緩存策略配置對象
*/
private Cache cache;
/**
* 參數映射,外部以何種形式對當前MappedStatement傳參
*/
private ParameterMap parameterMap;
/**
* 結果映射列表,應該是只有一個的,不明白為啥是列表,可能是多個結果集返回時使用的。
*/
private List<ResultMap> resultMaps;
/**
* 是否要刷新緩存,將其設置為 true 后,只要語句被調用,都會導致本地緩存和二級緩存被清空
* 對select命令,默認值為false,對insert、update、delete默認為true。
*/
private boolean flushCacheRequired;
/**
* 將其設置為 true 后,將會導致本條語句的結果被二級緩存緩存起來,默認值:對 select 元素為 true。
*/
private boolean useCache;
/**
* sql命令類型:如select、update、insert、delete等
*/
private SqlCommandType sqlCommandType;
//……
/**
* 語言驅動,如xml。
*/
private LanguageDriver lang;
/**
* 結果集類型列表
*/
private String[] resultSets;
}
復制代碼
~~~
### MappedStatement是怎么來的?
還是以XML配置方式為例進行分析,簡單說下源碼查找的過程。Mapper對應的SQL語句定義在xml文件中,順著源碼會發現完成xml解析工作的是XMLMapperBuilder,其中對xml中“select|insert|update|delete”類型元素的解析方法為buildStatementFromContext;buildStatementFromContext使用了XMLStatementBuilder類對statement進行解析,并最終創建了MappedStatement。
所以,XMLStatementBuilder#parseStatementNode方法就是我們分析的重點。但是,在此之前需要有一點準備工作要做。由于MappedStatement最終是由MapperBuilderAssistant構建的,它其中存儲了一些Mapper級別的共享信息并應用到MappedStatement中。所以,先來簡單了解下它的由來:
~~~java
public class XMLMapperBuilder extends BaseBuilder {
private final XPathParser parser;
private final MapperBuilderAssistant builderAssistant;
//省略部分字段和方法
public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
configuration, resource, sqlFragments);
}
private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
super(configuration);
this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
this.parser = parser;
this.sqlFragments = sqlFragments;
this.resource = resource;
}
//省略部分字段和方法
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
//省略部分代碼
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
}
}
復制代碼
~~~
* 代碼14行:MapperBuilderAssistant由XMLMapperBuilder的私有構造方法創建,這里傳入了全局Configuration對象和xml資源文件路徑;
* 代碼24~28行:設置builderAssistant的namespace。這個namespace就是我們在mapper xml中聲明的,也就是我們Mapper接口的完全限定名,如com.raysonxin.dao.CompanyDao。
* 代碼30行:開始了聲明節點的解析。
好了,下面正是開始XMLStatementBuilder#parseStatementNode的分析了。**為了節省篇幅,我直接通過代碼注釋的方式進行說明了,部分我認為不關鍵或不常用的內容沒有多說**。
~~~java
/**
* parseStatementNode方法是對select、insert、update、delete這四類元素進行解析,大體分為三個過程:
* 1、解析節點屬性:如我們最常用的id、resultMap等;
* 2、解析節點內的sql語句:首先把sql語句中包含的<include></include>等標簽轉為實際的sql語句,然后執行靜態或動態節點處理;
* 3、根據以上解析到的內容,使用builderAssistant創建MappedStatement,并加入Configuration中。
* <p>
* 以上過程中最關鍵的是第二步,它會根據實際使用的標簽,把sql片段轉為不同的SqlNode,以鏈表方式存儲到SqlSource中。
*/
public void parseStatementNode() {
//獲取標簽的id屬性,如selectById,對應Mapper接口中的方法名稱
String id = context.getStringAttribute("id");
//獲取databaseId屬性,我們一般都沒有寫。
String databaseId = context.getStringAttribute("databaseId");
/**
* 這段代碼雖然不起眼,但是一定要進去看一下:其內部完成了對id的再次賦值,
* 處理的方式是:id=namespace+"."+id,也就是當前Mapper的完全限定名+"."+id,
* 比如我們之前例子中的com.raysonxin.dao.CompanyDao.selectById
* 這也是Mapper接口中不能存在重載方法的根本原因。
* */
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
/**
* 下面這塊代碼會依次獲取fetchSize、timeout、resultMap等屬性,
* 需要注意的是,有些屬性雖然我們沒有設置,但是mybatis會設置默認值,
* 具體可以查看mybatis的官方說明。
*/
Integer fetchSize = context.getIntAttribute("fetchSize");
Integer timeout = context.getIntAttribute("timeout");
String parameterMap = context.getStringAttribute("parameterMap");
String parameterType = context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = resolveClass(parameterType);
String resultMap = context.getStringAttribute("resultMap");
String resultType = context.getStringAttribute("resultType");
String lang = context.getStringAttribute("lang");
//默認值:XMLLanguageDriver
LanguageDriver langDriver = getLanguageDriver(lang);
Class<?> resultTypeClass = resolveClass(resultType);
String resultSetType = context.getStringAttribute("resultSetType");
//默認值:PREPARED
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
//默認值:DEFAULT
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
String nodeName = context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
boolean useCache = context.getBooleanAttribute("useCache", isSelect);
boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
// Include Fragments before parsing
/**
* 英文注釋也說了,在sql解析前處理 include 標簽,比如說,我們include了BaseColumns,
* 它會把這個include標簽替換為BaseColumns內的sql內容
* */
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());
// Parse selectKey after includes and remove them.
//處理selectKey,主要針對不同的數據庫引擎做處理
processSelectKeyNodes(id, parameterTypeClass, langDriver);
// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
/**
* 到了關鍵步驟了:就是通過這句代碼完成了從xml標簽到SqlSource的轉換,
* SqlSource是一個接口,這里返回的可能是DynamicSqlSource、也可能是RawSqlSource,
* 取決于xml標簽中是否包含動態元素,比如 <if test=""></if>
* */
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
String resultSets = context.getStringAttribute("resultSets");
//下面這些是針對selectKey、KeyGenerator等進行處理,暫時跳過了。
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
KeyGenerator keyGenerator;
String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
if (configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = configuration.getKeyGenerator(keyStatementId);
} else {
keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
}
/**
* 節點及屬性都解析完成了,使用builderAssistant創建MappedStatement,
* 并保存到Configuration#mappedStatements。
* */
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
復制代碼
~~~
我們在xml中定義的select等語句就是通過這個parseStatementNode方法解析為MappedStatement的,整體來看比較容易理解,核心就是SqlSource的創建過程,大家可以寫個簡單的例子一步一步調試看下。
### SqlSource是什么,如何創建的?
SqlSource是整個MappedStatement的核心,MappedStatement其他一大堆字段都是為了準確的執行它而定義的。SqlSource是個半成品的sql語句,因為對于其中的動態標簽還沒靜態化,其中的參數也未賦值。正是如此,才為我們后續的調用執行提供了基礎,接下來重點看看SqlSource的構建過程。為了先從整體上了解,我畫了一個時序圖來描述SqlSource的解析、創建過程。

這個過程涉及三個參與者XMLStatementBuilder、XMLLanguageDriver、XMLScriptBuilder,核心在于XMLScriptBuilder對標簽的識別與解析,我們重點看XMLScriptBuilder#parseScriptNode這個方法,如下:
~~~java
/**
* parseScriptNode字面意思,解析sql腳本節點。
* */
public SqlSource parseScriptNode() {
// parseDynamicTags處理節點中的動態標簽,其實動態、靜態標簽都會讀取,
// 只是對于動態標簽會使用對應的動態標簽處理器解析。
MixedSqlNode rootSqlNode = parseDynamicTags(context);
SqlSource sqlSource = null;
// 如果是動態的,創建DynamicSqlSource;否則創建RawSqlSource。
// isDynamic默認是false,parseDynamicTags處理中,只要存在動態元素,他就被置為true
if (isDynamic) {
sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
} else {
sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
}
return sqlSource;
}
復制代碼
~~~
剛才一直再說SqlNode,那SqlNode到底是什么呢?結合一個例子,我們先簡單認識一下它,同樣放一張類圖(圖中并沒有放全)來了解其家族。

~~~xml
<select id="selectById" resultMap="baseResultMap" >
select
<include refid="BaseColumns"></include>
from company
<if test="id != null">
where id= #{id}
</if>
</select>
復制代碼
~~~
對于這個示例中的select標簽的sql語句(只看2-7行),mybatis會按照標簽完整性(閉合)解析為多個節點,同時根據節點中出現的元素類型創建不同類型的節點(依據是《Document Object Model (DOM) Level 3 Core Specification》中定義的12中類型),比如例子中,純文本解析為TextSqlNode,if標簽解析為IfSqlNode。
但是,我們在編寫sql語句時大多數情況是多種類型混合的,所以就有了MixedSqlNode,它以List存儲了所有的節點。parseDynamicTags方法的作用就是解析sql語句中的節點類型,并最終生成MixedSqlNode。
~~~java
protected MixedSqlNode parseDynamicTags(XNode node) {
List<SqlNode> contents = new ArrayList<>();
NodeList children = node.getNode().getChildNodes();
//遍歷節點
for (int i = 0; i < children.getLength(); i++) {
//獲取當前節點
XNode child = node.newXNode(children.item(i));
//判斷節點類型是否為CDATA_SECTION_NODE或TEXT_NODE
if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
//獲取節點的sql語句內容
String data = child.getStringBody("");
//創建TextSqlNode
TextSqlNode textSqlNode = new TextSqlNode(data);
//判斷是否為動態節點,實際判斷是否包含${}占位符,有就是true
if (textSqlNode.isDynamic()) {
contents.add(textSqlNode);
//設置為動態sql
isDynamic = true;
} else {
//不是動態節點,創建StaticTextSqlNode
contents.add(new StaticTextSqlNode(data));
}
}
//節點類型是否為ELEMENT_NODE
else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
//獲取節點的名稱,比如if
String nodeName = child.getNode().getNodeName();
//獲取對應的處理器,如IfHandler
XMLScriptBuilder.NodeHandler handler = nodeHandlerMap.get(nodeName);
if (handler == null) {
throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
}
//調用處理器,解析動態節點內容,這里面也會遞歸調用parseDynamicTags,逐層處理。
handler.handleNode(child, contents);
//設置為動態sql
isDynamic = true;
}
}
//創建MixedSqlNode
return new MixedSqlNode(contents);
}
復制代碼
~~~
總結一下parseDynamicTags方法的處理過程,parseDynamicTags僅處理類型為CDATA\_SECTION\_NODE、TEXT\_NODE、ELEMENT\_NODE的節點。
* 前兩種類型會首先作為TextSqlNode,若節點中沒有占位符`${}`,則轉為StaticTextSqlNode;若節點中有占位符`${}`,節點類型為TextSqlNode不變,但是會將這條sql設置為dynamic。這會導致后續的參數設置方式不同,引出`#{}`、`${}`的差別,我們在下一節在說明。
* 對ELEMENT\_NODE節點,會根據節點名稱(if、choose等)找到對應的處理器解析為動態節點,處理器有IfHandler、WhereHandler等9種。
所以,回到XMLScriptBuilder#parseScriptNode方法,根據isDynamic的值,會創建不同類型的SqlSource。到目前為止,我們知道創建DynamicSqlSource有兩種情況:一是sql節點包含if、choose、where這里動態標簽時;二是使用了`${}`占位符時。其余情況會創建RawSqlSource。
好了,SqlSource的創建過程我們就分析完了,在翻上去看看那張時序圖加深一下印象吧。
SqlSource創建完成后,就剩下builderAssistant#addMappedStatement這個過程了,比較簡單,大家可以自己查看源碼了解一下,我就不廢話了。
### 本文總結
正如文章開頭所說,本文的主要目的是打基礎,弄清楚是什么,為什么,為以后的怎么做做好鋪墊。本文分析了MappedStatement主要字段及作用、Mybatis如何創建MappedStatement、MappedStatement中的SqlSource是什么及創建過程幾個問題,雖然寫的很啰嗦,但是問題應該是描述的差不多了,希望能對大家的理解有一些幫助。
作者:碼路印記
鏈接:https://juejin.cn/post/6886805132936216583
來源:掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
- 一.JVM
- 1.1 java代碼是怎么運行的
- 1.2 JVM的內存區域
- 1.3 JVM運行時內存
- 1.4 JVM內存分配策略
- 1.5 JVM類加載機制與對象的生命周期
- 1.6 常用的垃圾回收算法
- 1.7 JVM垃圾收集器
- 1.8 CMS垃圾收集器
- 1.9 G1垃圾收集器
- 2.面試相關文章
- 2.1 可能是把Java內存區域講得最清楚的一篇文章
- 2.0 GC調優參數
- 2.1GC排查系列
- 2.2 內存泄漏和內存溢出
- 2.2.3 深入理解JVM-hotspot虛擬機對象探秘
- 1.10 并發的可達性分析相關問題
- 二.Java集合架構
- 1.ArrayList深入源碼分析
- 2.Vector深入源碼分析
- 3.LinkedList深入源碼分析
- 4.HashMap深入源碼分析
- 5.ConcurrentHashMap深入源碼分析
- 6.HashSet,LinkedHashSet 和 LinkedHashMap
- 7.容器中的設計模式
- 8.集合架構之面試指南
- 9.TreeSet和TreeMap
- 三.Java基礎
- 1.基礎概念
- 1.1 Java程序初始化的順序是怎么樣的
- 1.2 Java和C++的區別
- 1.3 反射
- 1.4 注解
- 1.5 泛型
- 1.6 字節與字符的區別以及訪問修飾符
- 1.7 深拷貝與淺拷貝
- 1.8 字符串常量池
- 2.面向對象
- 3.關鍵字
- 4.基本數據類型與運算
- 5.字符串與數組
- 6.異常處理
- 7.Object 通用方法
- 8.Java8
- 8.1 Java 8 Tutorial
- 8.2 Java 8 數據流(Stream)
- 8.3 Java 8 并發教程:線程和執行器
- 8.4 Java 8 并發教程:同步和鎖
- 8.5 Java 8 并發教程:原子變量和 ConcurrentMap
- 8.6 Java 8 API 示例:字符串、數值、算術和文件
- 8.7 在 Java 8 中避免 Null 檢查
- 8.8 使用 Intellij IDEA 解決 Java 8 的數據流問題
- 四.Java 并發編程
- 1.線程的實現/創建
- 2.線程生命周期/狀態轉換
- 3.線程池
- 4.線程中的協作、中斷
- 5.Java鎖
- 5.1 樂觀鎖、悲觀鎖和自旋鎖
- 5.2 Synchronized
- 5.3 ReentrantLock
- 5.4 公平鎖和非公平鎖
- 5.3.1 說說ReentrantLock的實現原理,以及ReentrantLock的核心源碼是如何實現的?
- 5.5 鎖優化和升級
- 6.多線程的上下文切換
- 7.死鎖的產生和解決
- 8.J.U.C(java.util.concurrent)
- 0.簡化版(快速復習用)
- 9.鎖優化
- 10.Java 內存模型(JMM)
- 11.ThreadLocal詳解
- 12 CAS
- 13.AQS
- 0.ArrayBlockingQueue和LinkedBlockingQueue的實現原理
- 1.DelayQueue的實現原理
- 14.Thread.join()實現原理
- 15.PriorityQueue 的特性和原理
- 16.CyclicBarrier的實際使用場景
- 五.Java I/O NIO
- 1.I/O模型簡述
- 2.Java NIO之緩沖區
- 3.JAVA NIO之文件通道
- 4.Java NIO之套接字通道
- 5.Java NIO之選擇器
- 6.基于 Java NIO 實現簡單的 HTTP 服務器
- 7.BIO-NIO-AIO
- 8.netty(一)
- 9.NIO面試題
- 六.Java設計模式
- 1.單例模式
- 2.策略模式
- 3.模板方法
- 4.適配器模式
- 5.簡單工廠
- 6.門面模式
- 7.代理模式
- 七.數據結構和算法
- 1.什么是紅黑樹
- 2.二叉樹
- 2.1 二叉樹的前序、中序、后序遍歷
- 3.排序算法匯總
- 4.java實現鏈表及鏈表的重用操作
- 4.1算法題-鏈表反轉
- 5.圖的概述
- 6.常見的幾道字符串算法題
- 7.幾道常見的鏈表算法題
- 8.leetcode常見算法題1
- 9.LRU緩存策略
- 10.二進制及位運算
- 10.1.二進制和十進制轉換
- 10.2.位運算
- 11.常見鏈表算法題
- 12.算法好文推薦
- 13.跳表
- 八.Spring 全家桶
- 1.Spring IOC
- 2.Spring AOP
- 3.Spring 事務管理
- 4.SpringMVC 運行流程和手動實現
- 0.Spring 核心技術
- 5.spring如何解決循環依賴問題
- 6.springboot自動裝配原理
- 7.Spring中的循環依賴解決機制中,為什么要三級緩存,用二級緩存不夠嗎
- 8.beanFactory和factoryBean有什么區別
- 九.數據庫
- 1.mybatis
- 1.1 MyBatis-# 與 $ 區別以及 sql 預編譯
- Mybatis系列1-Configuration
- Mybatis系列2-SQL執行過程
- Mybatis系列3-之SqlSession
- Mybatis系列4-之Executor
- Mybatis系列5-StatementHandler
- Mybatis系列6-MappedStatement
- Mybatis系列7-參數設置揭秘(ParameterHandler)
- Mybatis系列8-緩存機制
- 2.淺談聚簇索引和非聚簇索引的區別
- 3.mysql 證明為什么用limit時,offset很大會影響性能
- 4.MySQL中的索引
- 5.數據庫索引2
- 6.面試題收集
- 7.MySQL行鎖、表鎖、間隙鎖詳解
- 8.數據庫MVCC詳解
- 9.一條SQL查詢語句是如何執行的
- 10.MySQL 的 crash-safe 原理解析
- 11.MySQL 性能優化神器 Explain 使用分析
- 12.mysql中,一條update語句執行的過程是怎么樣的?期間用到了mysql的哪些log,分別有什么作用
- 十.Redis
- 0.快速復習回顧Redis
- 1.通俗易懂的Redis數據結構基礎教程
- 2.分布式鎖(一)
- 3.分布式鎖(二)
- 4.延時隊列
- 5.位圖Bitmaps
- 6.Bitmaps(位圖)的使用
- 7.Scan
- 8.redis緩存雪崩、緩存擊穿、緩存穿透
- 9.Redis為什么是單線程、及高并發快的3大原因詳解
- 10.布隆過濾器你值得擁有的開發利器
- 11.Redis哨兵、復制、集群的設計原理與區別
- 12.redis的IO多路復用
- 13.相關redis面試題
- 14.redis集群
- 十一.中間件
- 1.RabbitMQ
- 1.1 RabbitMQ實戰,hello world
- 1.2 RabbitMQ 實戰,工作隊列
- 1.3 RabbitMQ 實戰, 發布訂閱
- 1.4 RabbitMQ 實戰,路由
- 1.5 RabbitMQ 實戰,主題
- 1.6 Spring AMQP 的 AMQP 抽象
- 1.7 Spring AMQP 實戰 – 整合 RabbitMQ 發送郵件
- 1.8 RabbitMQ 的消息持久化與 Spring AMQP 的實現剖析
- 1.9 RabbitMQ必備核心知識
- 2.RocketMQ 的幾個簡單問題與答案
- 2.Kafka
- 2.1 kafka 基礎概念和術語
- 2.2 Kafka的重平衡(Rebalance)
- 2.3.kafka日志機制
- 2.4 kafka是pull還是push的方式傳遞消息的?
- 2.5 Kafka的數據處理流程
- 2.6 Kafka的腦裂預防和處理機制
- 2.7 Kafka中partition副本的Leader選舉機制
- 2.8 如果Leader掛了的時候,follower沒來得及同步,是否會出現數據不一致
- 2.9 kafka的partition副本是否會出現腦裂情況
- 十二.Zookeeper
- 0.什么是Zookeeper(漫畫)
- 1.使用docker安裝Zookeeper偽集群
- 3.ZooKeeper-Plus
- 4.zk實現分布式鎖
- 5.ZooKeeper之Watcher機制
- 6.Zookeeper之選舉及數據一致性
- 十三.計算機網絡
- 1.進制轉換:二進制、八進制、十六進制、十進制之間的轉換
- 2.位運算
- 3.計算機網絡面試題匯總1
- 十四.Docker
- 100.面試題收集合集
- 1.美團面試常見問題總結
- 2.b站部分面試題
- 3.比心面試題
- 4.騰訊面試題
- 5.哈羅部分面試
- 6.筆記
- 十五.Storm
- 1.Storm和流處理簡介
- 2.Storm 核心概念詳解
- 3.Storm 單機版本環境搭建
- 4.Storm 集群環境搭建
- 5.Storm 編程模型詳解
- 6.Storm 項目三種打包方式對比分析
- 7.Storm 集成 Redis 詳解
- 8.Storm 集成 HDFS 和 HBase
- 9.Storm 集成 Kafka
- 十六.Elasticsearch
- 1.初識ElasticSearch
- 2.文檔基本CRUD、集群健康檢查
- 3.shard&replica
- 4.document核心元數據解析及ES的并發控制
- 5.document的批量操作及數據路由原理
- 6.倒排索引
- 十七.分布式相關
- 1.分布式事務解決方案一網打盡
- 2.關于xxx怎么保證高可用的問題
- 3.一致性hash原理與實現
- 4.微服務注冊中心 Nacos 比 Eureka的優勢
- 5.Raft 協議算法
- 6.為什么微服務架構中需要網關
- 0.CAP與BASE理論
- 十八.Dubbo
- 1.快速掌握Dubbo常規應用
- 2.Dubbo應用進階
- 3.Dubbo調用模塊詳解
- 4.Dubbo調用模塊源碼分析
- 6.Dubbo協議模塊