黑馬點評雙攔截器和Threadlocal實現原理

文章目錄

    • 雙攔截器
    • ThreadLocal實現原理

雙攔截器

實現登錄狀態刷新的原因:

? 防止用戶會話過期:通過動態刷新Token有效期,確保活躍用戶不會因固定過期時間而被強制登出

? 提升用戶體驗:用戶無需頻繁重新登錄,只要在活動期間就能保持登錄狀態

在登錄狀態校驗時,只設置了一個攔截器:

只設置一個攔截器時:攔截器只攔截刷新需要登錄的路徑,如果用戶在登錄后長時間不訪問任何需要攔截的路徑,那么他們的登錄令牌可能不會得到及時刷新,導致令牌過期。一旦用戶嘗試訪問需要攔截的路徑,他們可能會發現自己需要重新登錄,因為令牌已經失效。所以單獨創建一個攔截器攔截一切請求,刷新Redis中的Key

雙攔截器執行流程:

那么我們可以添加一個攔截器,第一個攔截器攔截所有路徑,首先從請求頭中獲取token,如果token存在,則通過token查詢redis判斷判斷該token是否存在redis中(有可能是別的網站的token,所以要判斷是否在redis中)和有沒有過期(過期了會采用過期淘汰策略…有可能直接查不到/查到過期了就直接刪除),如果token存在reids中且沒有過期,將用戶信息(UserDTO對象,包括用戶id、昵稱等)保存到theadlocal然后刷新token有效期并放行,如果token不存在或過期了則不執行任何操作并放行。

第一個攔截器只進行刷新token操作不攔截,第二個攔截器攔截需要登錄的路徑,判斷ThreadLocal中的是否存在用戶信息,如果用戶存在就說明登錄了就放行,否則執行攔截操作。(只創建了一個threadlocal線程對象,threadlocalmap中只存了一個對象,直接判斷是否為空就可以)

token來源:注冊登錄后,后端會生成一個token作為用戶的唯一id,將這個token作為key用戶信息作為value存入redis中(還設置有效期),同時將這個token返回給前端,前端的每次請求都會攜帶這個token進行登錄狀態校驗操作。

Threadlocal中存入的用戶信息的作用

  1. 在第二個攔截器中可以用來判斷是否存在用戶信息,進而完成用戶登錄攔截操作
  2. 在后面一人一單判斷過程中,需要從Threadlocal中取出用戶id來構造用戶級細粒度鎖。

未登錄攔截的實現:如果需要登錄攔截,則返回HTTP狀態碼為401(未授權),前端根據狀態碼跳轉到登錄頁。

攔截器使用:1.定義攔截器 2.注冊配置攔截器

雙攔截器通過設置優先級來實現執行的先后順序(第一個攔截器優先級高order=0,第二個攔截器order=1)

ThreadLocal實現原理

客戶端每一次發起的請求都是單獨的一個線程,所以可以用ThreadLocal

ThreadLocal 是 Java 中的一個工具類,通過threadlocal類可以創建線程對象,threadlocal會在每個線程內開辟一個內存空間去保存每個線程的數據,可以實現線程間的數據隔離,避免線程安全問題。

ThreadLocal是用于解決線程安全的一種機制,它允許創建線程局部變量,每個線程自己獨立的變量副本,從而避免了線程之間的資源共享和同步問題。

這里說的副本指的是每個線程擁有該變量的獨立實例,線程之間不會共享相同的變量,從而實現了線程隔離和數據安全。

ThreadLocal 的作用

  1. 線程隔離: 每個線程擁有自己的變量副本,互不干擾。
  2. 避免共享: 無需使用鎖或同步機制,提升并發性能。
  3. 簡化設計: 方便在多線程環境中傳遞上下文信息(如用戶會話、事務 ID)。

ThreadLocal 的實現原理

主要是通過Thread類中的ThreadLocalMap字段來實現的。

  • ThreadLocalMap: 每個線程內部都有自己的 ThreadLocalMap,用于存儲 ThreadLocal 變量,一個線程可以創建多個ThreadLocal線程對象,如ThreadLocal1、ThreadLocal2等,存在ThreadLocalMap中的不同位置。
  • 鍵值對存儲ThreadLocal對象本身作為鍵,變量副本作為值。

ThreadLocal 的常用方法

  1. public void set(T value) 設置當前線程的線程局部變量的值
  2. public T get() 返回當前線程所對應的線程局部變量的值
  3. public void remove() 移除當前線程的線程局部變量

使用場景

  1. 線程上下文傳遞: 如用戶會話、事務 ID。
  2. 數據庫連接管理: 每個線程使用獨立的數據庫連接。
  3. 日期格式化SimpleDateFormat 非線程安全,可使用 ThreadLocal 為每個線程創建獨立實例。

ThreadLocalMap 只由數組組成,通過開放地址法中的線性探測(線性向后查找)的方式解決hash沖突。具體的:如果 i 位置被占用,嘗試 i+1。如果 i+1 也被占用,繼續探測 i+2,直到找到一個空位。如果到達數組末尾,則回到數組頭部,繼續尋找空位。

為什么用線性探測法而不用hashmap的拉鏈法?因為ThreadLocalMap 不會有大量的 Key,所以采用線性探測更節省空間。

GC 之后 key 是否為 null? 是null,因為key是弱引用,gc回收后,key為null,但是value是強引用,垃圾回收后還會存在。

用完之后要及時執行remove方法

ThreadLocalMap 擴容機制

采用的是“先清理再擴容”的策略,元素個數達到閾值(0.75*總容量)時,會先清理掉被垃圾回收掉key的entry對象,然后再檢查size是否到閾值,擴容時,數組長度翻倍,并重新計算索引,如果發生哈希沖突,采用線性探測法來解決。

使用 InheritableThreadLocal 時,會在創建子線程時,令子線程繼承父線程中的 ThreadLocal 值,但是無法支持線程池場景下的 ThreadLocal 值傳遞。

還有TransmittableThreadLocal:TransimittableTreadLocal 是 TreadLocal 的增強。它與InheritableThreadLocal 相比,更適合在線程池中父線程與子線程傳遞的場景。ITL 只是在子線程被創建時繼承一次父線程的值,之后如果子線程自己修改了值,就會一直復用這個值,不會拉取父線程的值,并且也感知不到父線程值得變化。而 TTL,是任務級別的動態捕獲,每次任務提交時,會動態捕獲父線程的最新值。通過捕獲上下文、傳遞上下文、恢復上下文的方式完成。 鏈接

每個線程都維護一個ThreadLocalMap,一個線程可以創建多個線程對象
public class MultipleThreadLocalsDemo {// 定義多個ThreadLocal變量private static final ThreadLocal<String> userContext = new ThreadLocal<>();private static final ThreadLocal<Integer> requestId = new ThreadLocal<>();private static final ThreadLocal<SimpleDateFormat> dateFormat = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));public static void main(String[] args) {// 主線程設置多個ThreadLocal值userContext.set("用戶A");requestId.set(1001);// 獲取值(互不干擾)System.out.println(userContext.get());  // 輸出"用戶A"System.out.println(requestId.get());    // 輸出1001System.out.println(dateFormat.get().format(new Date())); // 輸出當前日期// 必須顯式清理(防止內存泄漏)userContext.remove();requestId.remove();dateFormat.remove();}
}

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

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

相關文章

Windows 中動態庫.dll 的 .lib 文件有什么作用?

在 Windows 平臺開發中, 動態鏈接庫(Dynamic Link Library, DLL)。與之相關的還有一個常讓人困惑的文件——.lib 文件。那么,這個 .lib 文件到底有什么作用呢? 一、什么是 .lib 文件? .lib 文件是 靜態導入庫(Import Library) 文件,它通常與動態鏈接庫(DLL)一起生成…

細說STM32單片機FreeRTOS消息緩沖區及其應用實例

目錄 一、消息緩沖區功能概述 二、消息緩沖區操作相關函數 1、相關函數概述 2、部分函數詳解 &#xff08;1&#xff09;創建消息緩沖區 &#xff08;2&#xff09;寫入消息 &#xff08;3&#xff09;讀取消息 &#xff08;4&#xff09;消息緩沖區狀態查詢 三、消息…

【緩存】JAVA本地緩存推薦Caffeine和Guava

&#x1f31f; 引言 在軟件開發過程中&#xff0c;緩存是提升系統性能的常用手段。對于基礎場景&#xff0c;直接使用 Java集合框架&#xff08;如Map/Set/List&#xff09;即可滿足需求。然而&#xff0c;當面對更復雜的緩存場景時&#xff1a; 需要支持多種過期策略&#x…

IDA插件 MIPSROP的安裝和使用方法

前言 筆者的IDA版本為9.0&#xff0c;剛開始根據一些博客描述以為將mipsrop.py拷貝到IDA的plugins目錄即可&#xff0c;可操作后發現事情好像沒這么簡單&#xff0c;復制進去后就發現沒有博客中所說的 MIPS ROP Finder &#xff0c;筆者在網上搜索了很多博客后在 https://bbs.…

(1)轉置后,行列式的值不變 (2)將行列式的任意兩行互換位置后,行列式改變符號

以下是對原始內容在不改變內容本身的前提下進行的格式優化&#xff0c;以提升可讀性和邏輯清晰度&#xff1a; ? 行列式的幾何意義 行列式&#xff08;determinant&#xff09;是線性代數中一個非常重要的概念&#xff0c;它的幾何含義可以從以下幾個方面理解&#xff1a; &a…

最大似然估計(Maximum Likelihood Estimation, MLE)詳解

一、定義 最大似然估計 是一種參數估計方法&#xff0c;其核心思想是&#xff1a; 選擇能使觀測數據出現概率最大的參數值作為估計值。 具體來說&#xff0c;假設數據 D x 1 , x 2 , … , x n D{x_1,x_2,…,x_n} Dx1?,x2?,…,xn?獨立且服從某個概率分布 P ( x ∣ θ ) P(…

用go從零構建寫一個RPC(3)--異步調用+多路復用實現

在前兩個版本中&#xff0c;我們實現了基礎的客戶端-服務端通信、連接池、序列化等關鍵模塊。為了進一步提升吞吐量和并發性能&#xff0c;本版本新增了 異步發送機制 和 多路復用支持&#xff0c;旨在減少資源消耗、提升連接利用率。 代碼地址&#xff1a;https://github.com/…

FFmpeg 安裝包全攻略:gpl、lgpl、shared、master 區別詳解

這些 FFmpeg 安裝包有很多版本和變種&#xff0c;主要區別在于以下幾個方面&#xff1a; ? 一、從名稱中看出的關鍵參數&#xff1a; 1. 版本號 master&#xff1a;開發版&#xff0c;最新功能&#xff0c;但可能不穩定。n6.1 / n7.1&#xff1a;正式版本&#xff0c;更穩定…

深度學習實戰:從圖像分類到文本生成的完整案例解析

1 圖像分類案例 1.1 CIFAR10數據集介紹 cifar數據是torchvision第三方包提供的數據集 訓練集5w 測試集1w y標簽 10個類別 10分類問題 一張圖形狀 (32, 32, 3) import torch import torch.nn as nn from torchvision.datasets import CIFAR10 from torchvision.transforms i…

Android 添加系統服務的完整流程

[應用程序] (應用進程)│↓ 調用簡單API [SoundManager] │ ├─ 代理模式門面模式&#xff08;應用進程&#xff09;│ ├─ 緩存數據 ←─ 裝飾器模式&#xff08;應用進程&#xff09;│ └─ 轉換異常 ←─ 適配器模式&#xff08;應用進程&#xff09;│↓ 通過Bind…

wan2.1代碼筆記

GPU內存不夠&#xff0c;可以先運行umt5&#xff0c;然后再運行wanpipeline&#xff0c;參考FLUX.1代碼筆記&#xff0c;或者使用ComfyUI。 下面使用隨機數代替umt5 embedding。 import torch from diffusers.utils import export_to_video from diffusers import Autoencoder…

環境搭建與工具配置

3.1 本地環境搭建 3.1.1 WAMP環境搭建漏洞靶場&#xff08;一、二&#xff09; WAMP&#xff08;Windows Apache MySQL PHP&#xff09;是搭建本地Web漏洞靶場的基礎環境。 安裝步驟&#xff1a; Apache&#xff1a;下載并安裝最新版Apache HTTP Server&#xff0c;配置監…

STM32F446主時鐘失效時DAC輸出異常現象解析與解決方案

—### 現象概述 在STM32F446微控制器應用中&#xff0c;若主時鐘&#xff08;HSE&#xff09;的晶體信號對地短路&#xff0c;但DAC&#xff08;數模轉換器&#xff09;仍能輸出變化信號&#xff0c;這一現象看似矛盾&#xff0c;實則與系統時鐘切換機制密切相關。本文將從硬件…

React 如何封裝一個可復用的 Ant Design 組件

文章目錄 前言一、為什么需要封裝組件&#xff1f;二、 仿antd組件的Button按鈕三、封裝一個可復用的表格組件 (實戰)1. 明確需求2. 設計組件 API3. 實現組件代碼4. 使用組件 三、封裝組件的最佳實踐四、進階優化 總結 前言 作為一名前端開發工程師&#xff0c;在日常項目中&a…

STC89C52RC/LE52RC

STC89C52RC 芯片手冊原理圖擴展版原理圖 功能示例LED燈LED燈的常亮效果LED燈的閃爍LED燈的跑馬燈效果&#xff1a;從左到右&#xff0c;從右到左 數碼管靜態數碼管數碼管計數mian.cApp.cApp.hCom.cCom.hDir.cDir.hInt.cInt.hMid.cMid.h 模板mian.cApp.cApp.hCom.cCom.hDir.cDir…

踩坑記錄:RecyclerView 局部刷新notifyItemChanged多次調用只觸發一次 onBindViewHolder 的原因

1. 問題背景 在做項目的時候&#xff0c;RecyclerView需要使用局部刷新&#xff0c;使用 notifyItemChanged(position, payload) 實現局部刷新&#xff0c;但發現調用多次只執行了一次&#xff0c;第二個刷新不生效。 2. 錯誤示例&#xff08;只處理 payloads.get(0)&#xff…

OpenLayers 加載鷹眼控件

注&#xff1a;當前使用的是 ol 5.3.0 版本&#xff0c;天地圖使用的key請到天地圖官網申請&#xff0c;并替換為自己的key 地圖控件是一些用來與地圖進行簡單交互的工具&#xff0c;地圖庫預先封裝好&#xff0c;可以供開發者直接使用。OpenLayers具有大部分常用的控件&#x…

WPF···

設置啟動頁 默認最后一個窗口關閉,程序退出,可以設置 修改窗體的icon圖標 修改項目exe圖標 雙擊項目名會看到代碼 其他 在A窗體點擊按鈕打開B窗體,在B窗體設置WindowStartupLocation=“CenterOwner” 在A窗體的代碼設置 B.Owner = this; B.Show(); B窗體生成在A窗體中間…

github公開項目爬取

import requestsdef search_github_repositories(keyword, tokenNone, languageNone, max_results1000):"""通過 GitHub API 搜索倉庫&#xff0c;支持分頁獲取所有結果&#xff08;最多 1000 條&#xff09;:param keyword: 搜索關鍵詞:param token: GitHub To…

防震基座在半導體晶圓制造設備拋光機詳細應用案例-江蘇泊蘇系統集成有限公司

在半導體制造領域&#xff0c;晶圓拋光作為關鍵工序&#xff0c;對設備穩定性要求近乎苛刻。哪怕極其細微的振動&#xff0c;都可能對晶圓表面質量產生嚴重影響&#xff0c;進而左右芯片制造的成敗。以下為您呈現一個防震基座在半導體晶圓制造設備拋光機上的經典應用案例。 企…