RequestContextHolder 与 HttpServletRequest 的联系

ops/2024/12/19 16:42:19/

1. 什么是 RequestContextHolder?

  • RequestContextHolderSpring 框架 提供的一个工具类,用于在当前线程中存储和获取与请求相关的上下文信息。
  • 它是基于 ThreadLocal 实现的,能够保证每个线程独立存储和访问请求信息。

与 HttpServletRequest 的关系:

HttpServletRequest

  • 是标准的 Servlet API 提供的类,用于表示一个 HTTP 请求。
  • 在 Controller 方法中,我们通常通过参数注入来获取它:
    java">@GetMapping("/example")
    public String example(HttpServletRequest request) {String param = request.getParameter("name");return "Parameter: " + param;
    }
    

RequestContextHolder

  • 是 Spring 对请求上下文的封装。
  • 它的核心是通过 RequestAttributes(Spring 自定义的接口)来间接操作 HttpServletRequest,从而获取请求信息。
  • 它的最大优势是:在 Service 层、过滤器、拦截器 等不能直接注入 HttpServletRequest 的地方,也能获取请求信息。

2. RequestContextHolder 与 HttpServletRequest 的联系

核心联系

  • RequestContextHolder 不会直接持有 HttpServletRequest,但它持有与请求相关的上下文信息。
  • 这个上下文信息是通过 RequestAttributes 实现的,而 ServletRequestAttributes 是它的一个实现类,封装了 HttpServletRequestHttpServletResponse

示意图

java">HttpServletRequest  ↓  
ServletRequestAttributes(封装 HttpServletRequest)  ↓  
RequestContextHolder(通过 ThreadLocal 保存 ServletRequestAttributes)
  • 当 Spring 处理请求时,它会将 HttpServletRequest 封装成 ServletRequestAttributes,然后绑定到当前线程的 ThreadLocal 中。
  • RequestContextHolder 就是通过 ThreadLocal 拿到 ServletRequestAttributes,再从中获取 HttpServletRequest

3. RequestContextHolder 的工作原理

绑定请求上下文

Spring 在处理 HTTP 请求时,会自动把 HttpServletRequest 封装成 ServletRequestAttributes 并绑定到线程中:

java">ServletRequestAttributes attributes = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(attributes);

获取请求对象

我们可以通过 RequestContextHolder 拿到请求上下文,进一步获取 HttpServletRequest

java">ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();

总结RequestContextHolder 是一个中间桥梁,它通过 ServletRequestAttributes 间接地连接到 HttpServletRequest


4. RequestContextHolder 提供的主要方法

获取请求上下文

getRequestAttributes():获取当前线程保存的请求上下文。

示例

java">RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
if (attributes instanceof ServletRequestAttributes) {HttpServletRequest request = ((ServletRequestAttributes) attributes).getRequest();System.out.println("请求参数:" + request.getParameter("name"));
}
  • 返回类型是 RequestAttributes,需要强转为 ServletRequestAttributes 才能拿到 HttpServletRequest

Spring 设计 RequestAttributes 作为一个 通用接口,这样可以支持不同类型的请求上下文,而不仅仅是 HTTP 请求。

  • RequestAttributes:定义了请求上下文的通用方法,不依赖于特定类型的请求。
  • ServletRequestAttributesRequestAttributes 的一个具体实现,专门封装 Servlet 环境 下的 HttpServletRequestHttpServletResponse

为什么需要强制转换?

因为 RequestAttributes 接口 是通用的,它不知道自己具体是什么请求类型(例如 Servlet 请求、Portlet 请求等)。

  • RequestAttributes 只提供了通用方法,比如存储和获取请求属性。
  • ServletRequestAttributes 提供了专门的方法,比如 getRequest()getResponse(),用于获取 HttpServletRequestHttpServletResponse

currentRequestAttributes()

  • getRequestAttributes() 类似,但如果没有请求上下文,它会抛出异常。

RequestContextHolder 提供的方法

方法作用
getRequestAttributes()获取当前线程绑定的 RequestAttributes,如果没有返回 null
currentRequestAttributes()获取当前线程的 RequestAttributes,如果没有会抛出异常。
setRequestAttributes(RequestAttributes)手动将 RequestAttributes 绑定到当前线程。适用于手动设置上下文的场景。
resetRequestAttributes()清除当前线程绑定的 RequestAttributes,防止内存泄漏。
setRequestAttributes(RequestAttributes, boolean inheritable)支持将请求上下文传递给子线程。inheritable = true 表示上下文可继承。

5. 使用场景及代码示例

场景 1:在 Service 层获取 HttpServletRequest

通常情况下,Service 层不能直接访问 HttpServletRequest,但可以通过 RequestContextHolder 获取:

java">import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;public class RequestUtil {public static HttpServletRequest getCurrentRequest() {ServletRequestAttributes attributes =(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();return attributes != null ? attributes.getRequest() : null;}
}

使用

java">public void printRequestParam() {HttpServletRequest request = RequestUtil.getCurrentRequest();if (request != null) {String name = request.getParameter("name");System.out.println("请求参数 name:" + name);} else {System.out.println("无法获取 HttpServletRequest");}
}

场景 2:在异步线程中传递请求上下文

默认情况下,Spring 的请求上下文不会自动传递给新线程。需要手动设置:

java">import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;public class AsyncRequestExample {public void processInAsyncThread() {// 获取当前请求上下文RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();new Thread(() -> {try {// 绑定请求上下文到新线程RequestContextHolder.setRequestAttributes(attributes, true);// 获取 HttpServletRequestHttpServletRequest request =((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();System.out.println("异步线程中获取请求参数:" + request.getParameter("name"));} finally {// 清理请求上下文,防止内存泄漏RequestContextHolder.resetRequestAttributes();}}).start();}
}

场景 3:过滤器或拦截器中设置请求上下文

在自定义的过滤器中手动设置请求上下文:

java">import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;public class CustomFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {// 手动设置上下文RequestContextHolder.setRequestAttributes(new ServletRequestAttributes((HttpServletRequest) request));try {chain.doFilter(request, response);} finally {// 清理上下文,防止内存泄漏RequestContextHolder.resetRequestAttributes();}}
}

6. RequestContextHolder 的注意事项

  • 必须在请求线程中使用

    • RequestContextHolder 依赖于 ThreadLocal,只能在处理请求的线程中使用。
    • 如果在非 Web 环境或没有 HTTP 请求的线程中调用,会返回 null 或抛出异常。
  • 异步线程问题

    • 异步线程无法自动继承父线程的请求上下文,必须手动通过 setRequestAttributes 传递。
  • 内存泄漏风险

    • 在使用线程池时,如果不清理请求上下文(resetRequestAttributes),可能会导致内存泄漏。

http://www.ppmy.cn/ops/143227.html

相关文章

【安当产品应用案例100集】032-重塑企业SaaS平台的PostgreSQL凭据管理体系

一、案例背景 在本次案例分享中,一家为旅行社提供SaaS服务的技术服务商,其依赖PostgreSQL作为其核心数据存储解决方案,并且在阿里云和内网环境中均部署了相关服务与数据库实例。随着业务的发展和技术团队规模的扩大,当前的数据库…

富士相机基本参数学习

一色彩 富士相机视频调色入门课[上]|胶片模拟,白平衡与色彩|全是样片哦_哔哩哔哩_bilibili 步骤: 1设置曝光模式: 自动模式下拍摄降低难度 2设置白平衡:自动 不满意可以设置 3色彩&…

Scala学习记录

dao --------> 数据访问 mode ------> 模型 service ---->业务逻辑 Main -------> UI:用户直接操作,调用Service 改造UI层:

Envoy 服务发现原理大揭秘与核心要点概述

1 Envoy动态配置介绍 动态资源,是指由envoy通过xDS协议发现所需要的各项配置的机制,相关的配置信息保存 于称之为管理服务器(Management Server )的主机上,经由xDS API向外暴露;下面是一个 纯动态资源的基…

LeetCode 2475 数组中不等三元组的数目

问题描述: 给定一个下标从 0 开始的正整数数组 nums&#xff0c;我们的目标是找出并统计满足下述条件的三元组 (i, j, k) 的数目&#xff1a; 0 < i < j < k < nums.length&#xff0c;这确保了三元组索引的顺序性。nums[i]、nums[j] 和 nums[k] 两…

Github 2024-12-15 php开源项目日报Top10

根据Github Trendings的统计,今日(2024-12-15统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量PHP项目10Matomo:开源网站分析平台 创建周期:4687 天开发语言:PHP协议类型:GNU General Public License v3.0Star数量:18681 个Fork数量:…

linux 替换yum源镜像

1. 备份源镜像 sudo cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak 2. 下载国内镜像阿里云 如果没有wget可以用curl 代替 sudo wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo 清华大学 sudo wget -…

【云原生】Docker Compose 从入门到实战使用详解

目录 一、前言 二、Docker Compose 介绍 2.1 Docker Compose概述 2.2 Docker Compose特点 2.3 Docker Compose使用场景 三、Docker Compose 安装 3.1 安装docker环境 3.2 Docker Compose安装方式一 3.2.1 下载最新版 3.2.2 设置权限 3.2.3 设置软链接 3.2.4 查看版本…