1. 程式人生 > >資料庫中介軟體 Sharding-JDBC 原始碼分析 —— SQL 解析(一)之詞法解析

資料庫中介軟體 Sharding-JDBC 原始碼分析 —— SQL 解析(一)之詞法解析

本文主要基於 Sharding-JDBC 1.5.0 正式版

������關注微信公眾號:【芋道原始碼】有福利:
1. RocketMQ / MyCAT / Sharding-JDBC 所有原始碼分析文章列表
2. RocketMQ / MyCAT / Sharding-JDBC 中文註釋原始碼 GitHub 地址
3. 您對於原始碼的疑問每條留言將得到認真回覆。甚至不知道如何讀原始碼也可以請教噢
4. 新的原始碼解析文章實時收到通知。每週更新一篇左右

> 5. 認真的原始碼交流微信群。

1. 概述

SQL 解析引擎,資料庫中介軟體必備的功能和流程。Sharding-JDBC 在 1.5.0.M1

正式釋出時,將 SQL 解析引擎從 Druid 替換成了自研的。新引擎僅解析分片上下文,對於 SQL 採用”半理解”理念,進一步提升效能和相容性,同時降低了程式碼複雜度(不理解沒關係,我們後續會更新文章解釋該優點)。 國內另一款資料庫中介軟體 MyCAT SQL 解析引擎也是 Druid,目前也在開發屬於自己的 SQL 解析引擎。

可能有同學看到SQL 解析會被嚇到,請淡定,耐心往下看。《SQL 解析》內容我們會分成 5 篇相對簡短的文章,讓大家能夠相對輕鬆愉快的去理解:

  1. 詞法解析
  2. 插入 SQL 解析
  3. 查詢 SQL 解析
  4. 更新 SQL 解析
  5. 刪除 SQL 解析

SQL 解析引擎

parsing 包下,如上圖所見包含兩大元件:

  1. Lexer:詞法解析器。
  2. Parser:SQL解析器。

兩者都是解析器,區別在於 Lexer 只做詞法的解析,不關注上下文,將字串拆解成 N 個詞法。而 Parser 在 Lexer 的基礎上,還需要理解 SQL 。打個比方:

SQLSELECT * FROM t_user  
Lexer[SELECT] [ * ] [FROM] [t_user]  
Parser :這是一條 [SELECT] 查詢表為 [t_user] ,並且返回 [ * ] 所有欄位的 SQL

��不完全懂?沒關係,本文的主角是 Lexer,我們通過原始碼一點一點理解。一共 1400 行左右程式碼左右,還包含註釋等等,實際更少噢。

2. Lexer 詞法解析器

Lexer 原理順序順序順序 解析 SQL,將字串拆解成 N 個詞法。

核心程式碼如下:

// Lexer.java
public class Lexer {

    /**
     * 輸出字串
     * 比如:SQL
     */
    @Getter
    private final String input;
    /**
     * 詞法標記字典
     */
    private final Dictionary dictionary;
    /**
     * 解析到 SQL 的 offset
     */
    private int offset;
    /**
     * 當前 詞法標記
     */
    @Getter
    private Token currentToken;

    /**
     * 分析下一個詞法標記.
     *
     * @see #currentToken
     * @see #offset
     */
    public final void nextToken() {
        skipIgnoredToken();
        if (isVariableBegin()) { // 變數
            currentToken = new Tokenizer(input, dictionary, offset).scanVariable();
        } else if (isNCharBegin()) { // N\
            currentToken = new Tokenizer(input, dictionary, ++offset).scanChars();
        } else if (isIdentifierBegin()) { // Keyword + Literals.IDENTIFIER
            currentToken = new Tokenizer(input, dictionary, offset).scanIdentifier();
        } else if (isHexDecimalBegin()) { // 十六進位制
            currentToken = new Tokenizer(input, dictionary, offset).scanHexDecimal();
        } else if (isNumberBegin()) { // 數字(整數+浮點數)
            currentToken = new Tokenizer(input, dictionary, offset).scanNumber();
        } else if (isSymbolBegin()) { // 符號
            currentToken = new Tokenizer(input, dictionary, offset).scanSymbol();
        } else if (isCharsBegin()) { // 字串,例如:"abc"
            currentToken = new Tokenizer(input, dictionary, offset).scanChars();
        } else if (isEnd()) { // 結束
            currentToken = new Token(Assist.END, "", offset);
        } else { // 分析錯誤,無符合條件的詞法標記
            currentToken = new Token(Assist.ERROR, "", offset);
        }
        offset = currentToken.getEndPosition();
        // System.out.println("| " + currentToken.getLiterals() + " | " + currentToken.getType() + " | " + currentToken.getEndPosition() + " |");
    }
    /**
     * 跳過忽略的詞法標記
     * 1. 空格
     * 2. SQL Hint
     * 3. SQL 註釋
     */
    private void skipIgnoredToken() {
        // 空格
        offset = new Tokenizer(input, dictionary, offset).skipWhitespace();
        // SQL Hint
        while (isHintBegin()) {
            offset = new Tokenizer(input, dictionary, offset).skipHint();
            offset = new Tokenizer(input, dictionary, offset).skipWhitespace();
        }
        // SQL 註釋
        while (isCommentBegin()) {
            offset = new Tokenizer(input, dictionary, offset).skipComment();
            offset = new Tokenizer(input, dictionary, offset).skipWhitespace();
        }
    }
}

通過 #nextToken() 方法,不斷解析出 Token(詞法標記)。我們來執行一次,看看 SQL 會被拆解成哪些 Token。

SQL :SELECT i.* FROM t_order o JOIN t_order_item i ON o.order_id=i.order_id WHERE o.user_id=? AND o.order_id=?
literals TokenType類 TokenType值 endPosition
SELECT DefaultKeyword SELECT 6
i Literals IDENTIFIER 8
. Symbol DOT 9
* Symbol STAR 10
FROM DefaultKeyword FROM 15
t_order Literals IDENTIFIER 23
o Literals IDENTIFIER 25
JOIN DefaultKeyword JOIN 30
t_order_item Literals IDENTIFIER 43
i Literals IDENTIFIER 45
ON DefaultKeyword ON 48
o Literals IDENTIFIER 50
. Symbol DOT 51
order_id Literals IDENTIFIER 59
= Symbol EQ 60
i Literals IDENTIFIER 61
. Symbol DOT 62
order_id Literals IDENTIFIER 70
WHERE DefaultKeyword WHERE 76
o Literals IDENTIFIER 78
. Symbol DOT 79
user_id Literals IDENTIFIER 86
= Symbol EQ 87
? Symbol QUESTION 88
AND DefaultKeyword AND 92
o Literals IDENTIFIER 94
. Symbol DOT 95
order_id Literals IDENTIFIER 103
= Symbol EQ 104
? Symbol QUESTION 105
Assist END 105

眼尖的同學可能看到了 Tokenizer。對的,它是 Lexer 的好基佬,負責分詞

我們來總結下,Lexer#nextToken() 方法裡,使用 #skipIgnoredToken() 方法跳過忽略的 Token,通過 #isXXXX() 方法判斷好下一個 Token 的型別後,交給 Tokenizer 進行分詞返回 Token。‼️此處可以考慮做個優化,不需要每次都 new Tokenizer(...) 出來,一個 Lexer 搭配一個 Tokenizer。

由於不同資料庫遵守 SQL 規範略有不同,所以不同的資料庫對應不同的 Lexer。

子 Lexer 通過重寫方法實現自己獨有的 SQL 語法。

3. Token 詞法標記

上文我們已經看過 Token 的例子,一共有 3 個屬性:

  • TokenType type :詞法標記型別
  • String literals :詞法字面量標記
  • int endPosition :literals 在 SQL 裡的結束位置

TokenType 詞法標記型別,一共分成 4 個大類:

  • DefaultKeyword :詞法關鍵詞
  • Literals :詞法字面量標記
  • Symbol :詞法符號標記
  • Assist :詞法輔助標記

3.1 DefaultKeyword 詞法關鍵詞

不同資料庫有自己獨有的詞法關鍵詞,例如 MySQL 熟知的分頁 Limit。

我們以 MySQL 舉個例子,當建立 MySQLLexer 時,會載入 DefaultKeyword 和 MySQLKeyword( OracleLexer、PostgreSQLLexer、SQLServerLexer 同 MySQLLexer )。核心程式碼如下:

// MySQLLexer.java
public final class MySQLLexer extends Lexer {

    /**
     * 字典
     */
    private static Dictionary dictionary = new Dictionary(MySQLKeyword.values());

    public MySQLLexer(final String input) {
        super(input, dictionary);
    }
}

// Dictionary.java
public final class Dictionary {

    /**
     * 詞法關鍵詞Map
     */
    private final Map tokens = new HashMap<>(1024);

    public Dictionary(final Keyword... dialectKeywords) {
        fill(dialectKeywords);
    }

    /**
     * 裝上預設詞法關鍵詞 + 方言詞法關鍵詞
     * 不同的資料庫有相同的預設詞法關鍵詞,有有不同的方言關鍵詞
     *
     * @param dialectKeywords 方言詞法關鍵詞
     */
    private void fill(final Keyword... dialectKeywords) {
        for (DefaultKeyword each : DefaultKeyword.values()) {
            tokens.put(each.name(), each);
        }
        for (Keyword each : dialectKeywords) {
            tokens.put(each.toString(), each);
        }
    }
}

Keyword 與 Literals.IDENTIFIER 是一起解析的,我們放在 Literals.IDENTIFIER 處一起分析。

3.2 Literals 詞法字面量標記

Literals 詞法字面量標記,一共分成 6 種:

  • IDENTIFIER :詞法關鍵詞
  • VARIABLE :變數
  • CHARS :字串
  • HEX :十六進位制
  • INT :整數
  • FLOAT :浮點數

3.2.1 Literals.IDENTIFIER 詞法關鍵詞

詞法關鍵詞。例如:表名,查詢欄位 等等。

解析 Literals.IDENTIFIER 與 Keyword 核心程式碼如下:

// Lexer.java
private boolean isIdentifierBegin() {
   return isIdentifierBegin(getCurrentChar(0));
}
private boolean isIdentifierBegin(final char ch) {
   return CharType.isAlphabet(ch) || '`' == ch || '_' == ch || '$' == ch;
}

// Tokenizer.java
/**
* 掃描識別符號.
*
* @return 識別符號標記
*/
public Token scanIdentifier() {
   // `欄位`,例如:SELECT `id` FROM t_user 中的 `id`
   if ('`' == charAt(offset)) {
       int length = getLengthUntilTerminatedChar('`');
       return new Token(Literals.IDENTIFIER, input.substring(offset, offset + length), offset + length);
   }
   int length = 0;
   while (isIdentifierChar(charAt(offset + length))) {
       length++;
   }
   String literals = input.substring(offset, offset + length);
   // 處理 order / group 作為表名
   if (isAmbiguousIdentifier(literals)) {
       return new Token(processAmbiguousIdentifier(offset + length, literals), literals, offset + length);
   }
   // 從 詞法關鍵詞 查詢是否是 Keyword,如果是,則返回 Keyword,否則返回 Literals.IDENTIFIER
   return new Token(dictionary.findTokenType(literals, Literals.IDENTIFIER), literals, offset + length);
}
/**
* 計算到結束字元的長度
*
* @see #hasEscapeChar(char, int) 處理類似 SELECT a AS `b``c` FROM table。此處連續的 "``" 不是結尾,如果傳遞的是 "`" 會產生誤判,所以加了這個判斷
* @param terminatedChar 結束字元
* @return 長度
*/
private int getLengthUntilTerminatedChar(final char terminatedChar) {
   int length = 1;
   while (terminatedChar != charAt(offset + length) || hasEscapeChar(terminatedChar, offset + length)) {
       if (offset + length >= input.length()) {
           throw new UnterminatedCharException(terminatedChar);
       }
       if (hasEscapeChar(terminatedChar, offset + length)) {
           length++;
       }
       length++;
   }
   return length + 1;
}
/**
* 是否是 Escape 字元
*
* @param charIdentifier 字元
* @param offset 位置
* @return 是否
*/
private boolean hasEscapeChar(final char charIdentifier, final int offset) {
   return charIdentifier == charAt(offset) && charIdentifier == charAt(offset + 1);
}
private boolean isIdentifierChar(final char ch) {
   return CharType.isAlphabet(ch) || CharType.isDigital(ch) || '_' == ch || '$' == ch || '#' == ch;
}
/**
* 是否是引起歧義的識別符號
* 例如 "SELECT * FROM group",此時 "group" 代表的是表名,而非詞法關鍵詞
*
* @param literals 識別符號
* @return 是否
*/
private boolean isAmbiguousIdentifier(final String literals) {
   return DefaultKeyword.ORDER.name().equalsIgnoreCase(literals) || DefaultKeyword.GROUP.name().equalsIgnoreCase(literals);
}
/**
* 獲取引起歧義的識別符號對應的詞法標記型別
*
* @param offset 位置
* @param literals 識別符號
* @return 詞法標記型別
*/
private TokenType processAmbiguousIdentifier(final int offset, final String literals) {
   int i = 0;
   while (CharType.isWhitespace(charAt(offset + i))) {
       i++;
   }
   if (DefaultKeyword.BY.name().equalsIgnoreCase(String.valueOf(new char[] {charAt(offset + i), charAt(offset + i + 1)}))) {
       return dictionary.findTokenType(literals);
   }
   return Literals.IDENTIFIER;
}

3.2.2 Literals.VARIABLE 變數

變數。例如:SELECT @@VERSION

解析核心程式碼如下:

// Lexer.java
/**
* 是否是 變數
* MySQL 與 SQL Server 支援
* 
* @see Tokenizer#scanVariable()
* @return 是否
*/
protected boolean isVariableBegin() {
   return false;
}

// Tokenizer.java
/**
* 掃描變數.
* 在 MySQL 裡,@代表使用者變數;@@代表系統變數。
* 在 SQLServer 裡,有 @@。
*
* @return 變數標記
*/
public Token scanVariable() {
   int length = 1;
   if ('@' == charAt(offset + 1)) {
       length++;
   }
   while (isVariableChar(charAt(offset + length))) {
       length++;
   }
   return new Token(Literals.VARIABLE, input.substring(offset, offset + length), offset + length);
}

3.2.3 Literals.CHARS 字串

字串。例如:SELECT "123"

解析核心程式碼如下:

// Lexer.java
/**
* 是否 N\
* 目前 SQLServer 獨有:在 SQL Server 中處理 Unicode 字串常數時,必需為所有的 Unicode 字串加上前置詞 N
*
* @see Tokenizer#scanChars()
* @return 是否
*/
private boolean isNCharBegin() {
   return isSupportNChars() && 'N' == getCurrentChar(0) && '\'' == getCurrentChar(1);
}
private boolean isCharsBegin() {
   return '\'' == getCurrentChar(0) || '\"' == getCurrentChar(0);
}

// Tokenizer.java
/**
* 掃描字串.
*
* @return 字串標記
*/
public Token scanChars() {
   return scanChars(charAt(offset));
}
private Token scanChars(final char terminatedChar) {
   int length = getLengthUntilTerminatedChar(terminatedChar);
   return new Token(Literals.CHARS, input.substring(offset + 1, offset + length - 1), offset + length);
}

3.2.4 Literals.HEX 十六進位制

// Lexer.java
/**
* 是否是 十六進位制
*
* @see Tokenizer#scanHexDecimal()
* @return 是否
*/
private boolean isHexDecimalBegin() {
   return '0' == getCurrentChar(0) && 'x' == getCurrentChar(1);
}

// Tokenizer.java
/**
* 掃描十六進位制數.
*
* @return 十六進位制數標記
*/
public Token scanHexDecimal() {
   int length = HEX_BEGIN_SYMBOL_LENGTH;
   // 負數
   if ('-' == charAt(offset + length)) {
       length++;
   }
   while (isHex(charAt(offset + length))) {
       length++;
   }
   return new Token(Literals.HEX, input.substring(offset, offset + length), offset + length);
}

3.2.5 Literals.INT 整數

整數。例如:SELECT * FROM t_user WHERE id = 1

Literals.INT 與 Literals.FLOAT 是一起解析的,我們放在 Literals.FLOAT 處一起分析。

3.2.6 Literals.FLOAT 浮點數

浮點數。例如:SELECT * FROM t_user WHERE id = 1.0
浮點數包含幾種:”1.0”,”1.0F”,”7.823E5”(科學計數法)。

解析核心程式碼如下:

// Lexer.java
/**
* 是否是 數字
* '-' 需要特殊處理。".2" 被處理成省略0的小數,"-.2" 不能被處理成省略的小數,否則會出問題。
* 例如說,"SELECT a-.2" 處理的結果是 "SELECT" / "a" / "-" / ".2"
*
* @return 是否
*/
private boolean isNumberBegin() {
   return CharType.isDigital(getCurrentChar(0)) // 數字
           || ('.' == getCurrentChar(0) && CharType.isDigital(getCurrentChar(1)) && !isIdentifierBegin(getCurrentChar(-1)) // 浮點數
           || ('-' == getCurrentChar(0) && ('.' == getCurrentChar(0) || CharType.isDigital(getCurrentChar(1))))); // 負數
}

// Tokenizer.java
/**
* 掃描數字.
* 解析數字的結果會有兩種:整數 和 浮點數.
*
* @return 數字標記
*/
public Token scanNumber() {
   int length = 0;
   // 負數
   if ('-' == charAt(offset + length)) {
       length++;
   }
   // 浮點數
   length += getDigitalLength(offset + length);
   boolean isFloat = false;
   if ('.' == charAt(offset + length)) {
       isFloat = true;
       length++;
       length += getDigitalLength(offset + length);
   }
   // 科學計數表示,例如:SELECT 7.823E5
   if (isScientificNotation(offset + length)) {
       isFloat = true;
       length++;
       if ('+' == charAt(offset + length) || '-' == charAt(offset + length)) {
           length++;
       }
       length += getDigitalLength(offset + length);
   }
   // 浮點數,例如:SELECT 1.333F
   if (isBinaryNumber(offset + length)) {
       isFloat = true;
       length++;
   }
   return new Token(isFloat ? Literals.FLOAT : Literals.INT, input.substring(offset, offset + length), offset + length);
}

這裡要特別注意下:“-“。在數字表達例項,可以判定為 負號 和 減號(不考慮科學計數法)。

  • “.2” 解析結果是 “.2”
  • “-.2” 解析結果不能是 “-.2”,而是 “-” 和 “.2”。

3.3 Symbol 詞法符號標記

詞法符號標記。例如:”{“, “}”, “>=” 等等。

解析核心程式碼如下:

// Lexer.java
/**
* 是否是 符號
*
* @see Tokenizer#scanSymbol()
* @return 是否
*/
private boolean isSymbolBegin() {
   return CharType.isSymbol(getCurrentChar(0));
}

// CharType.java
/**
* 判斷是否為符號.
*
* @param ch 待判斷的字元
* @return 是否為符號
*/
public static boolean isSymbol(final char ch) {
   return '(' == ch || ')' == ch || '[' == ch || ']' == ch || '{' == ch || '}' == ch || '+' == ch || '-' == ch || '*' == ch || '/' == ch || '%' == ch || '^' == ch || '=' == ch
           || '>' == ch || '<' == ch || '~' == ch || '!' == ch || '?' == ch || '&' == ch || '|' == ch || '.' == ch || ':' == ch || '#' == ch || ',' == ch || ';' == ch;
}

// Tokenizer.java
/**
* 掃描符號.
*
* @return 符號標記
*/
public Token scanSymbol() {
   int length = 0;
   while (CharType.isSymbol(charAt(offset + length))) {
       length++;
   }
   String literals = input.substring(offset, offset + length);
   // 倒序遍歷,查詢符合條件的 符號。例如 literals = ";;",會是拆分成兩個 ";"。如果基於正序,literals = "<=",會被解析成 "<" + "="。
   Symbol symbol;
   while (null == (symbol = Symbol.literalsOf(literals))) {
       literals = input.substring(offset, offset + --length);
   }
   return new Token(symbol, literals, offset + length);
}

3.4 Assist 詞法輔助標記

Assist 詞法輔助標記,一共分成 2 種:

  • END :分析結束
  • ERROR :分析錯誤。

666. 彩蛋

知識星球

老鐵,是不是比想象中簡單一些?!繼續加油寫 Parser 相關的文章!來一波微信公眾號關注吧。

Sharding-JDBC 正在收集使用公司名單:傳送門。�� 你的登記,會讓更多人蔘與和使用 Sharding-JDBC。Sharding-JDBC 也會因此,能夠覆蓋更廣的場景。登記吧,少年!

**我建立了一個微信群【原始碼圈】,希望和大家分享交流讀原始碼的經驗。
讀原始碼先難後易,掌握方法後,可以做更有深度的學習。
而且掌握方法並不難噢。
加群方式:微信公眾號傳送關鍵字【qun】。**

������關注微信公眾號:【芋道原始碼】有福利:
1. RocketMQ / MyCAT / Sharding-JDBC 所有原始碼分析文章列表
2. RocketMQ / MyCAT / Sharding-JDBC 中文註釋原始碼 GitHub 地址
3. 您對於原始碼的疑問每條留言將得到認真回覆。甚至不知道如何讀原始碼也可以請教噢
4. 新的原始碼解析文章實時收到通知。每週更新一篇左右
5. 認真的原始碼交流微信群。

相關推薦

資料庫中介軟體 Sharding-JDBC 原始碼分析 —— SQL 解析查詢SQL

������關注微信公眾號:【芋艿的後端小屋】有福利: 1. RocketMQ / MyCAT / Sharding-JDBC 所有原始碼分析文章列表 2. RocketMQ / MyCAT / Sharding-JDBC 中文註釋原始碼

資料庫中介軟體 Sharding-JDBC 原始碼分析 —— SQL 解析詞法解析

本文主要基於 Sharding-JDBC 1.5.0 正式版 ������關注微信公眾號:【芋道原始碼】有福利: 1. RocketMQ / MyCAT / Sharding-JDBC 所有原始碼分析文章列表 2. Roc

資料庫中介軟體 Sharding-JDBC 原始碼分析 —— SQL 解析刪除SQL

本文主要基於 Sharding-JDBC 1.5.0 正式版 ������關注微信公眾號:【芋道原始碼】有福利: 1. RocketMQ / MyCAT / Sharding-JDBC 所有原始碼分析文章列表 2. Roc

資料庫分庫分表中介軟體 Sharding-JDBC 原始碼分析 —— 分散式主鍵

������關注微信公眾號:【芋道原始碼】有福利: 1. RocketMQ / MyCAT / Sharding-JDBC 所有原始碼分析文章列表 2. RocketMQ / MyCAT / Sharding-JDBC 中文註釋原始碼 Gi

數據庫中間件 Sharding-JDBC 源碼分析 —— SQL 解析語法解析

sharding-jdbc關註微信公眾號:【芋艿的後端小屋】有福利:RocketMQ / MyCAT / Sharding-JDBC 所有源碼分析文章列表RocketMQ / MyCAT / Sharding-JDBC 中文註釋源碼 GitHub 地址您對於源碼的疑問每條留言都將得到認真回復。甚至不知道如何讀

精盡Spring MVC原始碼分析 - HandlerMapping 元件 AbstractHandlerMapping

> 該系列文件是本人在學習 Spring MVC 的原始碼過程中總結下來的,可能對讀者不太友好,請結合我的原始碼註釋 [Spring MVC 原始碼分析 GitHub 地址](https://github.com/liu844869663/spring-framework) 進行閱讀 > > Spring

精盡Spring MVC原始碼分析 - HandlerAdapter 元件 HandlerAdapter

> 該系列文件是本人在學習 Spring MVC 的原始碼過程中總結下來的,可能對讀者不太友好,請結合我的原始碼註釋 [Spring MVC 原始碼分析 GitHub 地址](https://github.com/liu844869663/spring-framework) 進行閱讀 > > Spring

Lua原始碼分析 Gc篇原理

前言 原理 mark階段 sweep階段 三種顏色 資料流 參考 前言 已經有很多人寫了gc原始碼分析的文章了,自己為啥還要繼續寫呢?最主要的原因有兩個: 1.純屬對於個人來說,寫東西能夠加深自己的理解和記

開源網站流量統計系統Piwik原始碼分析——引數統計

  Piwik現已改名為,這是一套國外著名的開源網站統計系統,類似於百度統計、Google Analytics等系統。最大的區別就是可以看到其中的原始碼,這正合我意。因為我一直對統計的系統很好奇,很想知道里面的執行原理是怎麼樣的,碰巧了解到有這麼一個系統,因此馬上嘗試了一下。國內關於該系統的相關資料比較匱乏,

JUC原始碼分析-集合篇:ConcurrentHashMap

ConcurrentHashMap 是一個支援併發檢索和併發更新的執行緒安全的HashMap(但不允許空key或value)。不管是在實際工作或者是面試中,ConcurrentHashMap 都是在整個JUC集合框架裡出現頻率最高的一個類,所以,對ConcurrentHas

Spring 原始碼分析(三) —— AOPAOP原理

AOP概論         AOP(Aspect-Oriented Programming,面向切面的程式設計),談起AOP,則一定會追溯到OOP(Object Oriented Programming,面向物件程式設計),因為AOP可以說是對OOP的補充和完善,而這一切的

看透SpringMVC原始碼分析與實踐

一、網站架構及其演變過程   1.軟體的三大型別          軟體分為三個型別:單機軟體、BS結構的軟體(瀏覽器-服務端)、CS結構的軟體(客戶端-服務端)。 2.BS的基礎結構     &nb

springMVC原始碼分析--國際化LocaleResolver

        springMVC給我們提供了國際化支援,簡單來說就是設定整個系統的執行語言,然後根據系統的執行語言來展示對應語言的頁面,一般我們稱之為多語言。springMVC國際化機制就是可以設定整個系統的執行語言,其定義了一個國際化支援介面LocaleResolver

Nginx原始碼分析與實踐---編寫一個簡單的Http模組

在上一節中,我們通過修改配置檔案,便能讓nginx去訪問我們寫的html檔案,並返回給瀏覽器。問題是:nginx是如何檢測到我們寫的配置項的?檢測到後,nginx又是如何知道該進行什麼操作的? 本節通過親自實踐,寫一個經典的Hello World模組來了解相應的流程是如何進行的。我們採用自上

redis原始碼分析與思考——sds

  在閱讀黃健巨集的書《Redis設計與實現》的時候,深刻的意識到僅僅看別人的作品是遠遠不夠,自己更應該去閱讀原始碼,形成自己的思考,這樣才算真正的學進去了。   現如今,Nosql的概念大行其道,redis作為其中的佼佼者被廣大的開發者愛好著,而且Redis的原始碼僅僅只

Log4j2原始碼分析系列:配置載入

前言 在實際開發專案中,日誌永遠是一個繞不開的話題。本系列文章試圖以slf4j和log4j2日誌體系為例,從原始碼角度分析日誌工作原理。 學習日誌框架,首先要熟悉各類日誌框架,這裡推薦兩篇文章,就不再贅述了。 https://www.cnblogs.com/rjzheng/p/10042911.

ReentrantLock 原始碼分析以及 AQS

## 前言 JDK1.5 之後釋出了JUC(java.util.concurrent),用於解決多執行緒併發問題。AQS 是一個特別重要的同步框架,很多同步類都藉助於 AQS 實現了對執行緒同步狀態的管理。 AQS 中最主要的就是獨佔鎖和共享鎖的獲取和釋放,以及提供了一些可中斷的獲取鎖,超時等待鎖等方法。

精盡Spring MVC原始碼分析 - HandlerMapping 元件 HandlerInterceptor 攔截器

> 該系列文件是本人在學習 Spring MVC 的原始碼過程中總結下來的,可能對讀者不太友好,請結合我的原始碼註釋 [Spring MVC 原始碼分析 GitHub 地址](https://github.com/liu844869663/spring-framework) 進行閱讀 > > Spring

精盡Spring MVC原始碼分析 - HandlerMapping 元件 AbstractHandlerMethodMapping

> 該系列文件是本人在學習 Spring MVC 的原始碼過程中總結下來的,可能對讀者不太友好,請結合我的原始碼註釋 [Spring MVC 原始碼分析 GitHub 地址](https://github.com/liu844869663/spring-framework) 進行閱讀 > > Spring

精盡Spring MVC原始碼分析 - HandlerMapping 元件 AbstractUrlHandlerMapping

> 該系列文件是本人在學習 Spring MVC 的原始碼過程中總結下來的,可能對讀者不太友好,請結合我的原始碼註釋 [Spring MVC 原始碼分析 GitHub 地址](https://github.com/liu844869663/spring-framework) 進行閱讀 > > Spring