首页 > 其他分享 >RequestContextHolder 与 HttpServletRequest 的联系

RequestContextHolder 与 HttpServletRequest 的联系

时间:2024-12-17 18:27:36浏览次数:9  
标签:HttpServletRequest 联系 RequestContextHolder ServletRequestAttributes 线程 上下文 请求

1. 什么是 RequestContextHolder?

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

与 HttpServletRequest 的关系:

HttpServletRequest

  • 是标准的 Servlet API 提供的类,用于表示一个 HTTP 请求。
  • 在 Controller 方法中,我们通常通过参数注入来获取它:
    @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

示意图

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

3. RequestContextHolder 的工作原理

绑定请求上下文

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

ServletRequestAttributes attributes = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(attributes);

获取请求对象

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

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

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


4. RequestContextHolder 提供的主要方法

获取请求上下文

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

示例

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 获取:

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;
    }
}

使用

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 的请求上下文不会自动传递给新线程。需要手动设置:

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);
                
                // 获取 HttpServletRequest
                HttpServletRequest request =
                    ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
                System.out.println("异步线程中获取请求参数:" + request.getParameter("name"));
            } finally {
                // 清理请求上下文,防止内存泄漏
                RequestContextHolder.resetRequestAttributes();
            }
        }).start();
    }
}

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

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

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 {

    @Override
    public 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),可能会导致内存泄漏。

标签:HttpServletRequest,联系,RequestContextHolder,ServletRequestAttributes,线程,上下文,请求
From: https://blog.csdn.net/m0_73837751/article/details/144538725

相关文章

  • 12.8 每日总结 (非Web环境中尝试获取HttpServletRequest对象Bug)
    今天的Bug解决吧SpringAI的底层函数回调并没有注册到Web容器中,所以要通过Controller层提前传递。 这个问题出现的原因是因为你在非Web环境中尝试获取HttpServletRequest对象,而这个对象是Web应用程序特有的,用于封装客户端发送的HTTP请求信息。在Spring框架中,HttpServletRequest......
  • 网站底部电话修改密码,更新网站底部联系方式
    修改网站底部的电话号码通常涉及编辑网站的模板文件。以下是详细的步骤:登录FTP或文件管理器:使用FTP客户端(如FileZilla)或网站提供的文件管理器,登录到您的服务器。找到模板文件:导航到网站的模板目录,通常位于templates或themes文件夹中。找到与底部相关的文件,常见的文件名可能......
  • 网站的联系电话如何修改,如何修改网站上的联系电话
    修改网站上的联系电话可以确保访客能够及时联系到你。以下是详细的步骤:备份现有文件:在进行任何修改之前,确保备份所有网站文件和数据库。使用压缩工具(如WinRAR、7-Zip)创建备份文件。确保备份文件存储在安全的位置。登录FTP服务器:使用FTP客户端(如FileZilla)登录到网站服......
  • RequestContextHolder
    RequestContextHolder 是Spring框架中的一个工具类,它允许在没有显式传递请求对象的情况下,访问当前HTTP请求的上下文信息。它在一些需要访问当前请求但又不方便直接传递 HttpServletRequest 对象的场景中非常有用。基本概念RequestContextHolder 主要通过 ThreadLoca......
  • 织梦网站联系我们的修改,织梦CMS联系信息修改指南
    修改织梦CMS网站中的“联系我们”页面通常涉及编辑模板文件和内容。以下是详细的步骤:登录后台管理系统:使用管理员账号登录织梦CMS的后台管理系统。进入单页文档管理:进入“内容管理”->“单页文档管理”。编辑联系我们的页面:找到“联系我们”页面,点击“编辑”按......
  • 思维与感觉知觉的区别与联系
    在人类认识世界的过程中,思维与感觉知觉是两个不可或缺的心理过程。它们不仅是人类认知活动的基本组成部分,还在个体的日常生活中扮演着至关重要的角色。尽管思维与感觉知觉有着显著的区别,但它们之间的联系也是密切且复杂的。理解这两者的区别与联系,不仅有助于我们更好地认识人类......
  • 网站怎么修改域名电话,如何更改网站中的域名和联系电话
    修改网站中的域名和联系电话是保持网站信息准确的重要步骤。以下是详细的步骤:登录后台管理系统:打开浏览器,输入后台管理地址(如http://yourdomain.com/admin.php或http://yourdomain.com/wp-admin),使用管理员账号登录。导航到网站设置:在后台左侧菜单中,找到“设置”或“网......
  • 公司网站修改联系人,轻松几步完成
    当您需要修改公司网站上的联系人信息时,可以通过以下步骤轻松完成:登录后台管理:首先,确保您拥有足够的权限以访问网站的后台管理系统。使用您的账户信息登录。导航至联系人设置:登录后,导航至“联系我们”、“关于我们”或“设置”等相关页面。这些页面通常会包含联系人信息的编......
  • 网站底部电话修改,如何轻松更新网站底部的联系电话
    如果您需要修改网站底部的联系电话,可以按照以下步骤进行操作:登录后台管理:使用您的账户信息登录网站的后台管理系统。导航至底部设置:登录后,导航至“模板管理”或“页面管理”等相关页面。这些页面通常会包含底部内容的编辑功能。选择模板文件:在模板管理页面中,找到当前使用......
  • C++——哈希表(Hash Table),附加于 Python 中字典区别于联系
    哈希表(HashTable)是一种非常高效的数据结构,用于存储键值对(key-value)。允许我们以非常快的速度进行插入、删除和查找操作,因为这些操作的时间复杂度平均为O(1)。哈希表通过使用哈希函数将键映射到表中的位置,从而实现快速访问。一、【哈希表的基本概念】1、哈希函数:这是一个将......