黑马点评双拦截器和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中存入的用户信息的作用:
- 在第二个拦截器中可以用来判断是否存在用户信息,进而完成用户登录拦截操作
- 在后面一人一单判断过程中,需要从Threadlocal中取出用户id来构造用户级细粒度锁。
未登录拦截的实现:如果需要登录拦截,则返回HTTP状态码为401
(未授权),前端根据状态码跳转到登录页。
拦截器使用:1.定义拦截器 2.注册配置拦截器
双拦截器通过设置优先级来实现执行的先后顺序(第一个拦截器优先级高order=0,第二个拦截器order=1)
ThreadLocal实现原理
客户端每一次发起的请求都是单独的一个线程,所以可以用ThreadLocal
ThreadLocal 是 Java 中的一个工具类,通过threadlocal类可以创建线程对象,threadlocal会在每个线程内开辟一个内存空间去保存每个线程的数据,可以实现线程间的数据隔离,避免线程安全问题。
ThreadLocal是用于解决线程安全的一种机制,它允许创建线程局部变量,每个线程自己独立的变量副本,从而避免了线程之间的资源共享和同步问题。
这里说的副本指的是每个线程拥有该变量的独立实例,线程之间不会共享相同的变量,从而实现了线程隔离和数据安全。
ThreadLocal 的作用
- 线程隔离: 每个线程拥有自己的变量副本,互不干扰。
- 避免共享: 无需使用锁或同步机制,提升并发性能。
- 简化设计: 方便在多线程环境中传递上下文信息(如用户会话、事务 ID)。
ThreadLocal 的实现原理
主要是通过Thread类中的ThreadLocalMap字段来实现的。
- ThreadLocalMap: 每个线程内部都有自己的
ThreadLocalMap
,用于存储ThreadLocal
变量,一个线程可以创建多个ThreadLocal线程对象,如ThreadLocal1、ThreadLocal2等,存在ThreadLocalMap中的不同位置。 - 键值对存储:
ThreadLocal
对象本身作为键,变量副本作为值。
ThreadLocal 的常用方法
- public void set(T value) 设置当前线程的线程局部变量的值
- public T get() 返回当前线程所对应的线程局部变量的值
- public void remove() 移除当前线程的线程局部变量
使用场景
- 线程上下文传递: 如用户会话、事务 ID。
- 数据库连接管理: 每个线程使用独立的数据库连接。
- 日期格式化:
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();}
}
相关文章:

黑马点评双拦截器和Threadlocal实现原理
文章目录 双拦截器ThreadLocal实现原理 双拦截器 实现登录状态刷新的原因: 防止用户会话过期:通过动态刷新Token有效期,确保活跃用户不会因固定过期时间而被强制登出 提升用户体验:用户无需频繁重新登录,只要…...

港股IPO市场火爆 没有港卡如何参与港股打新?
据Wind资讯数据统计,今年1月1日至5月20日,港股共有23家企业IPO,较去年同期增加6家;IPO融资规模达600亿港元,较去年同期增长626.54%,IPO融资规模重回全球首位。 港股IPO市场持续火爆,不少朋友没有…...

RESTful API 在前后端交互中的作用与实践
一、RESTful API 概述 RESTful(Representational State Transfer)API 是一种基于 HTTP 协议、面向资源的架构风格,旨在实现前后端的松散耦合和高效通信。它通过定义统一的资源标识、操作方法以及数据传输格式,为前后端提供了一种…...
Jenkins+Docker+Harbor快速部署Spring Boot项目详解
JenkinsDockerHarbor快速部署Spring Boot项目详解 Jenkins、Docker和Harbor是现代DevOps流程中的核心工具,结合使用可以实现自动化构建、测试和部署。下面我将详细介绍如何搭建这个集成环境。 一、各工具的核心作用 Jenkins 自动化CI/CD工具,负责拉取代…...

python打卡训练营打卡记录day35
知识点回顾: 三种不同的模型可视化方法:推荐torchinfo打印summary权重分布可视化进度条功能:手动和自动写法,让打印结果更加美观推理的写法:评估模式 作业:调整模型定义时的超参数,对比下效果 1…...

如何评价OpenRouter这样的大模型API聚合平台?
OpenRouter通过统一接口简化多模型访问与集成的复杂性,实现一站式调用。然而,这种便利性背后暗藏三重挑战:成本控制、服务稳定性、对第三方供应商的强依赖性。 现在AI大模型火得一塌糊涂,新模型层出不穷,各有各的长处。但是对于开发者来说,挨个去对接OpenAI、谷歌、Anthr…...
恢复二叉搜索树:递归与中序遍历的智慧应用
恢复二叉搜索树:递归与中序遍历的智慧应用 二叉搜索树(BST)是一种在算法世界里相当重要的数据结构,它的特性——左子树的节点值小于根节点,而右子树的节点值大于根节点——让它在查找、插入和删除操作上都能高效运行。然而,现实总是充满意外,有时候由于错误的操作或数据…...
从零开始构建一个区块链应用:技术解析与实践指南
区块链技术自比特币诞生以来,已经逐渐从金融领域扩展到更多行业,如供应链管理、物联网、智能合约等。它以其去中心化、不可篡改和透明性等特点,吸引了众多开发者的关注。然而,对于初学者来说,区块链技术的学习曲线可能…...

5.2.4 wpf中MultiBinding的使用方法
在 WPF 中,MultiBinding 允许将多个绑定(Binding)组合成一个逻辑结果,并通过一个转换器(IMultiValueConverter)处理这些值,最终影响目标属性。以下是其核心用法和示例: 核心组件: MultiBinding:定义多个绑定源的集合。 IMultiValueConverter:实现逻…...

技术服务业-首套运营商网络路由5G SA测试专网搭建完成并对外提供服务
为了更好的服务蜂窝无线技术及运营商测试认证相关业务,搭建了技术服务业少有的5G测试专网,可独立灵活配置、完整端到端5G(含RedCap、LAN)的网络架构。 通过走真正运营商网络路由的方式,使终端设备的测试和运营商网络兼…...

仿腾讯会议——音频服务器部分
1、中介者定义处理音频帧函数 2、 中介者实现处理音频帧函数 3、绑定函数映射 4、服务器定义音频处理函数 5、 服务器实现音频处理函数...

大文件上传,对接阿里oss采用前端分片技术。完成对应需求!
最近做了一个大文件分片上传的功能,记录下 1. 首先是安装阿里云 oss 扩展 composer require aliyuncs/oss-sdk-php 去阿里云 oss 获取配置文件 AccessKey ID *** AccessKey Secret *** Bucket名称 *** Endpoint *** 2. 前端上传,对文件进行分片…...

【场景分析】基于概率距离快速削减法的风光场景生成与削减方法
目录 1 主要内容 场景消减步骤 2 部分代码 3 程序结果 1 主要内容 该程序参考文献《含风光水的虚拟电厂与配电公司协调调度模型》场景消减部分模型,程序对风电场景进行生成并采用概率距离方法进行消减,程序先随机生成200个风电出力场景,然…...

【Java Web】3.SpringBootWeb请求响应
📘博客主页:程序员葵安 🫶感谢大家点赞👍🏻收藏⭐评论✍🏻 文章目录 一、请求 1.1 postman 1.2 简单参数 1.3 实体参数 1.4 数组集合参数 1.5 日期参数 1.6 JSON参数 1.7 路径参数 二、响应 2…...

单片机中断系统工作原理及定时器中断应用
文件目录 main.c #include <REGX52.H> #include "TIMER0.H" #include "KEY.H" #include "DELAY.H"//void Timer0_Init() { // TMOD 0x01; // TL0 64536 % 256; // TH0 64536 / 256; // ET0 1; // EA 1; // TR0 1; //}unsigned char…...

LangGraph-agent-天气助手
用于创建agent和多代理工作流 循环(有迭代次数)、可控、持久 安装langgraph包 conda create --name agent python3.12 conda activate agent pip install -U langgraph pip install langchain-openai设置 windows(>结尾) s…...

深度学习——超参数调优
第一部分:什么是超参数?为什么要调优? 一、参数 vs 超参数(Parameter vs Hyperparameter) 类型定义举例是否通过训练自动学习?参数(Parameter)是模型在训练过程中通过反向传播自动…...
阿里云API RAG全流程实战:从模型调用到多模态应用的完整技术链路
一、引言 在企业级智能应用开发中,如何让大模型高效利用动态数据并生成准确回答,是构建智能问答系统的核心挑战。阿里云提供的API RAG(检索增强生成)流程,通过整合通义千问大模型、百炼智能体平台与知识库管理体系&am…...

创建型:建造者模式
目录 1、核心思想 2、实现方式 2.1 模式结构 2.2 工作流程 2.3 实现案例 2.4 变体:链式建造者(常见于多参数对象,无需指挥者) 3、优缺点分析 4、适用场景 1、核心思想 目的:将复杂对象的构建过程与其表示分离…...
Jenkins集成Docker与K8S构建
Jenkins 是一个开源的持续集成和持续交付(CI/CD)工具,广泛用于自动化软件开发过程中的构建、测试和部署任务。它通过插件系统提供了高度的可扩展性,支持与多种开发工具和技术的集成。 Jenkins 的核心功能 Jenkins 的主要功能包括自动化构建、测试和部署。它能够监控版本控…...
redis缓存实战-19(使用 Pub/Sub 构建简单的聊天应用程序)
实践练习:使用 Pub/Sub 构建简单的聊天应用程序 Redis Pub/Sub 是一项强大的功能,可在应用程序的不同部分之间实现实时通信。这是一种消息传递范例,其中发送方(发布者)不直接向特定接收方(订阅者)发送消息,而是将消息发布到通道。订阅者对一个或多个通道表示兴趣,并且…...

UE4游戏查找本地角色数据的方法-SDK
UE4中,玩家的表示通常涉及以下几个类: APlayerController: 代表玩家的控制逻辑,处理输入等。 APawn: 代表玩家在世界中的实体(比如一个角色、一辆车)。APlayerController 控制一个 APawn。 ACharacter: APawn 的一个…...
游园安排--最长上升子序列+输出序列
1.最长上升子序列,用二分贪心算法优化的那个 2.分割提取游客名字,与蓝肽子序列类似 3.关键是我不知道怎么输出答案序列,这里学习了一种不按序实现的,思想是倒序能凑上就加入,反正从ma开始遍历,生成一定是…...
缓存一致性与AI内容生成的幂等控制
缓存一致性与AI内容生成的幂等控制 在AI架构中,缓存系统作为提升响应速度与减少模型调用压力的关键组件,必须同时解决两个核心问题: 缓存一致性问题:数据源变动后,如何确保缓存及时更新、不过期、不脏读;…...

Java 连接并操作 Redis 万字详解:从 Jedis 直连到 RedisTemplate 封装,5 种方式全解析
引言 在分布式系统和高并发场景中,Redis 作为高性能内存数据库的地位举足轻重。对于 Java 开发者而言,掌握 Redis 的连接与操作是进阶必备技能。然而,从基础的 Jedis 原生客户端到 Spring 封装的 RedisTemplate,不同连接方式的原…...

python web 开发-Flask-Login使用详解
Flask-Login使用详解:轻松实现Flask用户认证 1. Flask-Login简介 Flask-Login是Flask框架的一个扩展,专门用于处理用户认证相关的功能。它提供了用户会话管理、登录/注销视图、记住我功能等常见认证需求,让开发者能够快速实现安全的用户认证…...

快速排序算法的C++和C语言对比
快速排序算法简介: 快速排序(Quick Sort)是一种高效的排序算法,采用分治法策略。它的基本思想是: 1. 从数列中挑出一个元素作为"基准" 2. 重新排序数列,所有比基准值小的元素放在基准前面,所有比基准值大的…...

分布式事务知识点整理
目录 分布式事务问题?问题场景引入分布式事务的理论标准BASE理论附CAP理论 Two-phase Commit,2PC2PC系统组件两阶段执行过程2PC缺点 Three-Phase Commit,3PC三阶段执行过程 TTC(Try-Confirm-Cancel)seata项目以及原理how to define a Distrib…...
微信小程序数据接收
1.微信小程序蓝牙模块中的 wx.onBLECharacteristicValueChange 回调函数有时候一个数据包会分多个数据包回调,有时候多个数据包会合并成一个数据包回调,如果接收到数据包就处理业务,分拆的和合并的数据都会因为解析失败,导致业…...

鸿蒙UI开发——badge角标的使用
1、概 述 badge小红点角标是我们项目开发中常见的需求,信息标记组件,可以附加在单个组件上用于信息提醒的容器组件。效果如下: 2、Badge 接口定义如下: 👉🏻 根据数字创建标记组件; Badge(v…...