RequestContextHolder 与 HttpServletRequest 的联系
1. 什么是 RequestContextHolder?[*]RequestContextHolder 是 Spring 框架 提供的一个工具类,用于在当火线程中存储和获取与哀求相干的上下文信息。
[*]它是基于 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 是它的一个实现类,封装了 HttpServletRequest 和 HttpServletResponse。
示意图
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:定义了哀求上下文的通用方法,不依赖于特定类型的哀求。
[*]ServletRequestAttributes:RequestAttributes 的一个详细实现,专门封装 Servlet 环境 下的 HttpServletRequest 和 HttpServletResponse。
为什么需要逼迫转换?
因为 RequestAttributes 接口 是通用的,它不知道自己详细是什么哀求类型(例如 Servlet 哀求、Portlet 哀求等)。
[*]RequestAttributes 只提供了通用方法,比如存储和获取哀求属性。
[*]ServletRequestAttributes 提供了专门的方法,比如 getRequest() 和 getResponse(),用于获取 HttpServletRequest 和 HttpServletResponse。
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),可能会导致内存泄漏。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]