支持异步线程自动传递上下文(例如当前请求)的工具类(支持自定义上下文传递逻辑,支持拦截所有异步操作)

文章目录

  • 支持异步线程自动传递上下文(例如当前请求)的工具类(支持自定义上下文传递逻辑,支持拦截所有异步操作)
    • 使用示范
    • ContextSupportedAsyncUtil .java
    • 自动拦截所有异步线程池操作
      • ContextSupportedExecutorAspect.java
    • 自定义上下文注入逻辑示范

支持异步线程自动传递上下文(例如当前请求)的工具类(支持自定义上下文传递逻辑,支持拦截所有异步操作)

当我们使用异步线程去执行一些耗时操作的时候,这些异步操作中可能需要获取当前请求等上下文信息
若未做传递逻辑默认是获取不到的,因此写了一个自动传递的工具和切面,可使用工具手动调用时会自动拷贝上下文信息,加载切面后会拦截所有异步操作自动拷贝上下文信息。
同时,上下文信息的加载拷贝移除逻辑也可实现接口自定义,自行扩展。

和阿里巴巴TransmittableThreadLocal(TTL)类似,多支持了可以自定义上下文传递逻辑,你可以认为是阿里TTL手写版本知乎介绍阿里TTL原理
在这里插入图片描述

使用示范

异步执行lambda表达式中的代码,代码中获取当前请求的URL并打印
若未使用该工具,是无法获取到当前请求的。

ContextSupportedAsyncUtil.execute(()->{System.out.println("我在异步线程中获取到的当前请求URL是"+((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getRequestURI());});

ContextSupportedAsyncUtil .java


import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;/*** @author humorchen* date: 2024/7/30* description: 支持上下文自动传递的异步工具* 默认支持自动传递 HttpServletRequest 到异步线程中* 其他上下文可自行创建类实现 AsyncContextInjector 接口,并调用ContextSupportedAsyncUtil.registerContextInjector 将其注册上去。* 可参考 SpringWebRequestContextInjector。class**/
@Slf4j
public class ContextSupportedAsyncUtil {private static final int CORE_SIZE = 8;private static final int MAX_SIZE = 32;private static final int QUEUE_SIZE = 1024;private static final int KEEP_ALIVE = 5;private static final TimeUnit KEEP_ALIVE_UNIT = TimeUnit.MINUTES;private static final ArrayBlockingQueue<Runnable> QUEUE = new ArrayBlockingQueue<>(QUEUE_SIZE);private static final AtomicInteger THREAD_NUM = new AtomicInteger(0);private static final RejectedExecutionHandler REJECT_POLICY = new ThreadPoolExecutor.CallerRunsPolicy();private static final List<Class<? extends AsyncContextInjector>> ASYNC_CONTEXT_INJECTOR_CLS_LIST = new ArrayList<>();private static ContextSupportedThreadPoolExecutor EXECUTOR = new ContextSupportedThreadPoolExecutor(CORE_SIZE, MAX_SIZE, KEEP_ALIVE, KEEP_ALIVE_UNIT, QUEUE, (r) -> new Thread(r, "AsyncUtil-thread-" + THREAD_NUM.incrementAndGet()), REJECT_POLICY);static {// 默认支持spring mvc 的RequestHolder自动传递到异步线程中ASYNC_CONTEXT_INJECTOR_CLS_LIST.add(SpringWebRequestContextInjector.class);}/*** 支持自定义上下文传递的executor*/public static class ContextSupportedThreadPoolExecutor extends ThreadPoolExecutor {public ContextSupportedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);}public ContextSupportedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);}public ContextSupportedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);}public ContextSupportedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);}/*** @param command the task to execute*/@Overridepublic void execute(@NonNull Runnable command) {super.execute(command instanceof AsyncRunnable ? command : getAsyncRunnable(command));}}/*** 异步上下文注入器接口* 将A线程的上下文注入到执行Runnable的B线程,并在执行后清除*/public interface AsyncContextInjector {/*** 初始化* 读取A线程的上下文并保存到当前对象*/void init();/*** 注入上下文信息* 注入init阶段存储的上下文到B线程的上下文中*/void inject();/*** 移除上下文信息* 清理B线程刚注入的上下文*/void remove();}/*** 支持传递ServletRequestAttributes对象用于获取当前请求HttpServletRequest*/public static class SpringWebRequestContextInjector implements AsyncContextInjector {private ServletRequestAttributes requestAttributes;/*** 初始化* 读取A线程的上下文并保存到当前对象*/@Overridepublic void init() {requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();}/*** 注入上下文信息* 注入init阶段存储的上下文到B线程的上下文中*/@Overridepublic void inject() {RequestContextHolder.setRequestAttributes(requestAttributes);}/*** 移除上下文信息* 清理B线程刚注入的上下文*/@Overridepublic void remove() {RequestContextHolder.resetRequestAttributes();}}/*** 抽象的注入器,将自定义的每个注入器执行*/public static class AbstractAsyncContextInjector {private final List<AsyncContextInjector> list = new ArrayList<>(ASYNC_CONTEXT_INJECTOR_CLS_LIST.size());public void init() {// 初始化每个异步上下文注入器for (Class<? extends AsyncContextInjector> aClass : ASYNC_CONTEXT_INJECTOR_CLS_LIST) {try {AsyncContextInjector asyncContextInjector = aClass.newInstance();asyncContextInjector.init();list.add(asyncContextInjector);} catch (Exception e) {log.error("AbstractAsyncContextInjector init error", e);}}}public void inject() {for (AsyncContextInjector asyncContextInjector : list) {try {asyncContextInjector.inject();} catch (Exception e) {log.error("AbstractAsyncContextInjector inject error", e);}}}public void remove() {for (AsyncContextInjector asyncContextInjector : list) {try {asyncContextInjector.remove();} catch (Exception e) {log.error("AbstractAsyncContextInjector remove error", e);}}}}/*** 支持异步线程传递自定义上下文时使用的Runnable包装类*/public static class AsyncRunnable extends AbstractAsyncContextInjector implements Runnable {private final Runnable runnable;public AsyncRunnable(Runnable runnable) {// 初始化保存A线程上下文信息init();this.runnable = runnable;}@Overridepublic void run() {if (runnable == null) {return;}try {// 将保存的A线程的上下文信息恢复到B线程inject();runnable.run();} catch (Exception e) {log.error("【ContextSupportedAsyncUtil】 run error {}", e.getMessage());throw e;} finally {// 清理B线程的上下文remove();}}}/*** 异步执行任务** @param runnable*/public static void execute(Runnable runnable) {EXECUTOR.execute(runnable);}/*** 异步执行任务** @param runnable*/public static void submit(Runnable runnable) {execute(runnable);}/*** 执行异步任务并获取返回值** @param supplier* @param <T>* @return*/public static <T> CompletableFuture<T> execute(Supplier<T> supplier) {return CompletableFuture.supplyAsync(supplier, EXECUTOR);}/*** 获取线程池** @return*/public static ThreadPoolExecutor getExecutor() {return EXECUTOR;}/*** 设置本工具使用的线程池** @param executor*/public static void setExecutor(ContextSupportedThreadPoolExecutor executor) {if (executor == null) {throw new NullPointerException("executor 不得为空");}if (executor.isShutdown() || executor.isTerminated() || executor.isTerminating()) {throw new IllegalStateException("executor 状态不得为shutdown");}ContextSupportedThreadPoolExecutor old = EXECUTOR;EXECUTOR = executor;if (old != null) {old.shutdown();}}/*** 获取支持上下文注入的Runnable** @param runnable* @return 包装后的runnable*/public static AsyncRunnable getAsyncRunnable(Runnable runnable) {return new AsyncRunnable(runnable);}/*** 注册注入器** @param cls*/public static void registerContextInjector(Class<? extends AsyncContextInjector> cls) {if (cls != null && !ASYNC_CONTEXT_INJECTOR_CLS_LIST.contains(cls)) {ASYNC_CONTEXT_INJECTOR_CLS_LIST.add(cls);}}
}

自动拦截所有异步线程池操作

ContextSupportedExecutorAspect.java

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;/*** @author humorchen* date: 2024/8/7* description: 支持上下文的executor**/
@Component
@Aspect
@Slf4j
public class ContextSupportedExecutorAspect {@Around("execution(* java.util.concurrent.Executor.execute(java.lang.Runnable))")public Object contextSupportedExecutor(ProceedingJoinPoint joinPoint) throws Throwable {Object[] args = joinPoint.getArgs();if (args != null && args.length > 0) {Object arg = args[0];if (arg instanceof Runnable && !(arg instanceof ContextSupportedAsyncUtil.AsyncRunnable)) {args[0] = ContextSupportedAsyncUtil.getAsyncRunnable((Runnable) arg);}}return joinPoint.proceed(args);}}

自定义上下文注入逻辑示范

SpringWebRequestContextInjector.java
异步线程上下文自动注入当前请求(默认提供)

/*** 支持传递ServletRequestAttributes对象用于获取当前请求HttpServletRequest*/public static class SpringWebRequestContextInjector implements AsyncContextInjector {private ServletRequestAttributes requestAttributes;/*** 初始化* 读取A线程的上下文并保存到当前对象*/@Overridepublic void init() {requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();}/*** 注入上下文信息* 注入init阶段存储的上下文到B线程的上下文中*/@Overridepublic void inject() {RequestContextHolder.setRequestAttributes(requestAttributes);}/*** 移除上下文信息* 清理B线程刚注入的上下文*/@Overridepublic void remove() {RequestContextHolder.resetRequestAttributes();}}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/144609.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【C++】——多态

文章目录 多态的概念多态的定义和实现虚函数虚函数的重写(覆盖)虚函数重写的例外 override 和 final关键字重载、重写和重定义(隐藏)纯虚函数和抽象类多态的原理动态绑定和静态绑定 多态的概念 多态就是多种形态&#xff0c;在执行某个行为时&#xff0c;当不同对象去完成时&a…

九章云极交付总监徐阳受邀为第四届中国项目经理大会演讲嘉宾

全国项目经理专业人士年度盛会 北京九章云极科技有限公司基础设施中心交付总监徐阳先生受邀为PMO评论主办的全国项目经理专业人士年度盛会——2024第四届中国项目经理大会演讲嘉宾&#xff0c;演讲议题为“进阶卓越&#xff1a;自我培养备受需求的项目经理的实战策略”。大会将…

靠AI视频在短视频平台接单月入过万,她怎么做到的?AI视频真的来了

大家好&#xff0c;我是画画的小强 相信大家和我一样&#xff0c;从小也有一个导演梦&#xff0c;感谢AI时代&#xff0c;替我完成了这个梦想&#xff0c;如果你想知道如何实现的&#xff0c;今天这篇文章&#xff0c;你一定要看完&#xff01; 从去年11月份起&#xff0c;随…

python函数三:拆包和交换变量值、引用、匿名函数

文章目录 1. 拆包和交换变量值1.1 拆包1.2 交换变量值 2. 引用2.1 了解引用2.1 把引用当作参数传递 3. 匿名函数3.1 lambda语法3.2 lambda的应用3.3 使用使用函数形式来求解某天是该年的第几天&#xff1f; 1. 拆包和交换变量值 1.1 拆包 拆包&#xff1a;把组合形成的元组形…

Fipexide(FPX):植物组织培养中的新兴化学诱导剂AbMole

在植物科学领域&#xff0c;组织培养技术一直是探索植物发育机制和生物技术应用的强大工具。然而&#xff0c;尽管植物生长调节剂如生长素&#xff08;Auxin&#xff09;和细胞分裂素&#xff08;Cytokinin&#xff09;在促进植物愈伤组织形成和再分化中发挥了关键作用&#xf…

数据结构之树(下),你真的懂吗?

数据结构入门学习&#xff08;全是干货&#xff09;——树&#xff08;下&#xff09; 1 堆 (Heap) 1.1 什么是堆 堆 (Heap) 是一种特殊的完全二叉树&#xff0c;分为最大堆和最小堆。 最大堆&#xff1a;每个节点的值都大于或等于其子节点的值&#xff0c;根节点是整个堆的…

一个实用的贴图工具Snipaste

Snipaste贴图工具操作指南 Snipaste 是一个简单但强大的贴图工具&#xff0c;同时也可以执行截屏、标注等功能。 一、安装与启动 下载Snipaste&#xff1a;访问 Snipaste 的官方网站下载合适的安装包。 安装&#xff1a;双击下载的安装包&#xff0c;按照提示完成安装过程。…

简单题88. 合并两个有序数组 (Python)20240920

问题描述&#xff1a; python&#xff1a; class Solution(object):def merge(self, nums1, m, nums2, n):""":type nums1: List[int]:type m: int:type nums2: List[int]:type n: int:rtype: None Do not return anything, modify nums1 in-place instead.&qu…

大模型微调是否具有技术含量?或者说其技术含量究竟有多少?

有句老生常谈的话&#xff1a;一项工作是否具有技术含量取决于你怎么做&#xff0c;这在大模型&#xff08;LLM&#xff09;方向上尤其如此&#xff0c;因为与传统自然语言处理&#xff08;NLP&#xff09;相比&#xff0c;它的上手门槛变得更低了。 我来举些例子&#xff0c;…

基于微信小程序的剧本杀游玩一体化平台

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 基于微信小程序JavaSpringBootVueMySQL的剧…

pdf编辑器免费版哪个好用?8款pdf编辑软件推荐指南,从入门到精通!

在现代数字化办公中&#xff0c;PDF格式以其稳定及兼容性成为了文档分享的首选。然而&#xff0c;处理PDF文件时&#xff0c;您是否曾感到困惑&#xff0c;不知如何进行编辑&#xff1f;无论是添加文本、替换图像&#xff0c;还是压缩文件&#xff0c;找到合适的工具都是关键。…

算法设计与分析(最长公共子序列

目录 最长公共子序列问题描述代码实现输出结果注意事项 小结&#xff1a; 最长公共子序列 最长公共子序列&#xff08;Longest Common Subsequence, LCS&#xff09;问题是计算给定两个序列的最长子序列的长度&#xff0c;这个子序列不要求连续&#xff0c;但需要保持相同的相…

【QML】qml splash 启动界面 实现代码

1. 工程结构 2. 现象 3. 代码 3.1 main.cpp #include <QGuiApplication> #include <QQmlApplicationEngine>int main(int argc, char *argv[]) { #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #…

华为OD机试 - 字符串划分(Python/JS/C/C++ 2024 E卷 100分)

华为OD机试 2024E卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试真题&#xff08;Python/JS/C/C&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;私信哪吒&#xff0c;备注华为OD&#xff0c;加入华为OD刷题交流群&#xff0c;…

C语言 结构体和共用体——典型实例:洗发牌模拟

目录 如何表示52张扑克牌&#xff1f; 如何保存一副扑克牌&#xff1f; 如何发牌&#xff1f; 如何设计主函数&#xff1f; 如何模拟洗牌&#xff1f; 如何表示52张扑克牌&#xff1f; 如何保存一副扑克牌&#xff1f; 如何发牌&#xff1f; 如何设计主函数&#xff1f; 如…

前端vue-父传子

父传子的话是在components中创建一个子组件MyTest.vue&#xff0c;并且在父组件中先导入(import MyTest from "./components/MyTest")&#xff0c;再注册&#xff08;在expo二default中写上 compnents:{MyTest}&#xff09;&#xff0c;再使用标签&#xff08;<My…

stm32单片机个人学习笔记5(OLED调试工具)

前言 本篇文章属于stm32单片机&#xff08;以下简称单片机&#xff09;的学习笔记&#xff0c;来源于B站教学视频。下面是这位up主的视频链接。本文为个人学习笔记&#xff0c;只能做参考&#xff0c;细节方面建议观看视频&#xff0c;肯定受益匪浅。 STM32入门教程-2023版 细…

指挥中心操作台怎么布局更合理

在现代社会&#xff0c;指挥中心作为应急响应、监控调度与决策支持的核心区域&#xff0c;其操作台的布局合理性直接关系到工作效率与应急反应速度。一个科学合理的操作台布局&#xff0c;不仅能够提升团队协作效率&#xff0c;还能在关键时刻为决策者赢得宝贵时间。那么&#…

【论文阅读】Slim Fly: A Cost Effective Low-Diameter Network Topology 一种经济高效的小直径网络拓扑

文章目录 Slim Fly: A Cost Effective Low-Diameter Network Topology文章总结1. 摘要2. indroduction3. 主要工作 主要思想references Slim Fly: A Cost Effective Low-Diameter Network Topology Slim Fly&#xff1a;一种经济高效的小直径网络拓扑 SC’14 Maciej Besta 苏…

吸尘器制造5G智能工厂物联数字孪生平台,推进制造业数字化转型

吸尘器制造行业&#xff0c;作为传统制造业的重要组成部分&#xff0c;也在积极探索如何通过先进技术实现生产模式的创新升级。5G智能工厂与物联数字孪生平台的融合应用&#xff0c;为吸尘器制造业的数字化转型铺设了一条高速通道&#xff0c;不仅极大地提升生产效率&#xff0…