RequestAttributes
RequestContextHolder是Spring Web模块提供的工具类,基于ThreadLocal存储当前线程的请求上下文(RequestAttributes)。可通过它在任意代码层获取当前HTTP请求的HttpServletRequest、HttpServletResponse等对象,无需依赖控制器层传递参数
基本方法
获取请求上下文对象
java
// 获取当前请求的RequestAttributes对象(非空,否则抛异常)
RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
// 安全获取(可能返回null)
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();直接获取请求/响应对象
java
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();操作请求作用域属性
java
// 获取/设置请求作用域属性(SCOPE_REQUEST 只在当前HTTP请求的生命周期内有效,SCOPE_SESSION 只在当前会话Session有效)
Object value = attributes.getAttribute("key", RequestAttributes.SCOPE_REQUEST);
attributes.setAttribute("key", value, RequestAttributes.SCOPE_REQUEST);线程间传递上下文
java
// 手动设置上下文到其他线程(需注意线程安全)
RequestContextHolder.setRequestAttributes(attributes);基本使用
在Service层获取请求信息
在权限校验、日志记录等场景中,直接从Service访问请求头或参数:
java
public void logUserAction() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String userAgent = request.getHeader("User-Agent");
// 记录日志...
}异步任务中传递请求上下文
通过DelegatingRequestContextRunnable或@Async配置,确保子线程能访问父线程的请求数据:
java
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
public class DelegatingRequestContextRunnable implements Runnable {
private final Runnable task;
private final RequestAttributes requestAttributes;
public DelegatingRequestContextRunnable(Runnable task) {
this.task = task;
this.requestAttributes = RequestContextHolder.getRequestAttributes();
}
@Override
public void run() {
try {
// 将主线程的请求上下文设置到子线程中
RequestContextHolder.setRequestAttributes(requestAttributes);
task.run();
} finally {
// 清理上下文,避免内存泄漏
RequestContextHolder.resetRequestAttributes();
}
}
}java
// 包装异步任务
Runnable task = new DelegatingRequestContextRunnable(() -> {
// 子线程中可以安全访问请求上下文
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
System.out.println("Request URI: " + request.getRequestURI());
// 处理异步逻辑...
});
// 提交任务到线程池
executorService.submit(task);全局获取当前用户信息
结合拦截器或过滤器,在登录后将用户信息存入请求作用域,后续任意位置直接获取:
java
// 拦截器中设置用户信息
request.setAttribute("currentUser", user);
// Service中获取
User user = (User) RequestContextHolder.currentRequestAttributes()
.getAttribute("currentUser", RequestAttributes.SCOPE_REQUEST);自定义注解解析
在AOP切面中,通过请求上下文实现动态逻辑:
java
@Around("@annotation(needAuth)")
public Object checkAuth(ProceedingJoinPoint joinPoint) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
if (!checkToken(request.getHeader("token"))) {
throw new UnauthorizedException();
}
return joinPoint.proceed();
}注意事项
- 非Web环境报错:在定时任务或测试中调用
currentRequestAttributes()会抛出IllegalStateException,建议先使用getRequestAttributes()判空 - 线程安全问题:手动调用
setRequestAttributes()时需确保线程隔离,避免并发修改 - 异步上下文传递:默认线程池会丢失上下文,需配合
TaskDecorator或Spring的@Async配置异步上下文传播 - 作用域生命周期:请求作用域(
SCOPE_REQUEST)在请求结束后自动清除,避免内存泄漏
