以下是详细的解释:
1. Attribute 的生命周期概述
HttpServletRequest Attribute 的生命周期仅限于单个 HTTP 请求范围内。具体来说:
-
开始:当请求到达服务器时创建
-
结束:当响应返回给客户端后销毁
-
作用域:仅在当前请求处理过程中有效
2. 完整的生命周期阶段
请求处理流程中的 Attribute 生命周期
// 1. 请求到达服务器 - Attribute 存储空间创建
// HttpServletRequest 对象被创建,内置一个空的 Attribute Map
// 2. Filter 链处理
public class LoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
// Filter 中可以设置 Attribute(最早可设置的位置)
httpRequest.setAttribute("requestStartTime", System.currentTimeMillis());
httpRequest.setAttribute("clientIp", getClientIpAddress(httpRequest));
chain.doFilter(request, response); // 传递给下一个 Filter 或 Servlet
}
}
// 3. Interceptor 预处理
@Component
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 在 Controller 执行前设置 Attribute
request.setAttribute("userInfo", getUserFromToken(request));
request.setAttribute("interceptorProcessed", true);
return true;
}
}
// 4. Controller 处理
@Controller
public class UserController {
@GetMapping("/user/{id}")
public String getUserProfile(@PathVariable String id, HttpServletRequest request) {
// 设置业务相关的 Attribute
request.setAttribute("userId", id);
request.setAttribute("pageTitle", "用户详情页");
request.setAttribute("userData", userService.findById(id));
// 此时可以获取之前设置的所有 Attribute
Long startTime = (Long) request.getAttribute("requestStartTime");
Object userInfo = request.getAttribute("userInfo");
return "user/profile";
}
}
// 5. 视图渲染(JSP/Thymeleaf等)
// 在视图模板中可以访问所有设置的 Attribute
视图模板中的使用示例
JSP 示例:
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<title>${pageTitle}</title>
</head>
<body>
<h1>用户ID: ${userId}</h1>
<p>用户名: ${userData.username}</p>
<p>处理时间: ${(System.currentTimeMillis() - requestStartTime)} ms</p>
</body>
</html>
Thymeleaf 示例:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title th:text="${pageTitle}">默认标题</title>
</head>
<body>
<h1 th:text="'用户ID: ' + ${userId}">用户ID</h1>
<p th:text="'用户名: ' + ${userData.username}">用户名</p>
</body>
</html>
3. Attribute 的基本操作方祛
@Controller
public class AttributeDemoController {
public void attributeOperations(HttpServletRequest request) {
// 设置 Attribute
request.setAttribute("stringAttr", "这是一个字符串");
request.setAttribute("numberAttr", 12345);
request.setAttribute("objectAttr", new User("张三", 25));
request.setAttribute("listAttr", Arrays.asList("A", "B", "C"));
// 获取 Attribute
String stringValue = (String) request.getAttribute("stringAttr");
Integer numberValue = (Integer) request.getAttribute("numberAttr");
User user = (User) request.getAttribute("objectAttr");
// 检查 Attribute 是否存在
if (request.getAttribute("userInfo") != null) {
// 存在时的处理
}
// 获取所有 Attribute 名称
Enumeration<String> attributeNames = request.getAttributeNames();
while (attributeNames.hasMoreElements()) {
String name = attributeNames.nextElement();
Object value = request.getAttribute(name);
System.out.println(name + " = " + value);
}
// 移除 Attribute
request.removeAttribute("tempAttr");
}
}
4. 不同技术栈中的 Attribute 使用
Spring MVC 中的便捷方式
@Controller
public class UserController {
// 使用 Model 或 ModelMap(最终都会设置到 Request Attribute 中)
@GetMapping("/user/{id}")
public String getUser(@PathVariable String id, Model model) {
// 以下操作等价于 request.setAttribute()
model.addAttribute("user", userService.findById(id));
model.addAttribute("pageTitle", "用户详情");
// 直接返回对象也会被添加到 Attribute 中(属性名为类名首字母小写)
model.addAttribute(userService.findById(id)); // 属性名为 "user"
return "user/details";
}
// 使用 ModelAndView
@GetMapping("/user/profile")
public ModelAndView getUserProfile() {
ModelAndView mav = new ModelAndView("user/profile");
mav.addObject("user", getCurrentUser());
mav.addObject("timestamp", System.currentTimeMillis());
return mav;
}
}
转发(Forward)时的 Attribute 传递
@Controller
public class ForwardController {
@GetMapping("/old-url")
public String forwardExample(HttpServletRequest request) {
// 设置 Attribute
request.setAttribute("message", "这是从旧URL传递的数据");
// 转发到新URL,Attribute 会保留
return "forward:/new-url";
}
@GetMapping("/new-url")
public String newUrlHandler(HttpServletRequest request) {
// 可以获取到转发前设置的 Attribute
String message = (String) request.getAttribute("message");
System.out.println("接收到的消息: " + message);
return "result-page";
}
}
包含(Include)时的 Attribute 处理
@Controller
public class IncludeController {
@GetMapping("/main-page")
public String mainPage(HttpServletRequest request) {
request.setAttribute("mainData", "主页面数据");
return "main-layout";
}
// 在 JSP 中包含其他页面
// <jsp:include page="/header" />
@GetMapping("/header")
public String headerFragment(HttpServletRequest request) {
// 可以访问主页面设置的 Attribute
String mainData = (String) request.getAttribute("mainData");
request.setAttribute("headerTitle", "网站标题 - " + mainData);
return "fragments/header :: header";
}
}
5. Attribute 的生命周期边界案例
重定向(Redirect)时 Attribute 不会传递
@Controller
public class RedirectController {
@GetMapping("/redirect-example")
public String redirectExample(HttpServletRequest request) {
// 设置 Attribute
request.setAttribute("importantData", "重要数据");
// 重定向到新URL(创建新请求)
return "redirect:/new-page"; // Attribute 会丢失!
}
@GetMapping("/new-page")
public String newPage(HttpServletRequest request) {
// 这里获取不到 importantData,因为重定向是新请求
Object data = request.getAttribute("importantData"); // null
return "new-page";
}
// 解决方案:使用 RedirectAttributes(Spring MVC)
@GetMapping("/redirect-with-flash")
public String redirectWithFlash(RedirectAttributes redirectAttributes) {
// 使用 Flash Attribute(通过 Session 临时存储)
redirectAttributes.addFlashAttribute("message", "重定向传递的数据");
return "redirect:/target-page";
}
}
异步请求中的 Attribute
@Controller
public class AsyncController {
@GetMapping("/async-data")
@ResponseBody
public Callable<String> asyncExample(HttpServletRequest request) {
// 在主线程中设置 Attribute
request.setAttribute("mainThreadData", "主线程数据");
return () -> {
// 在异步线程中
String data = (String) request.getAttribute("mainThreadData");
// 注意:在异步处理中,可能需要特殊处理来传递 Attribute
return "异步处理结果: " + data;
};
}
}
6. 实际应用场景示例
性能监控
@Component
public class TimingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
long startTime = System.currentTimeMillis();
request.setAttribute("requestStartTime", startTime);
chain.doFilter(request, response);
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
// 记录请求处理时间
System.out.println("请求处理时间: " + duration + "ms");
}
}
用户认证信息传递
@Component
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String token = request.getHeader("Authorization");
User user = authService.validateToken(token);
if (user != null) {
request.setAttribute("currentUser", user);
request.setAttribute("userId", user.getId());
request.setAttribute("userRoles", user.getRoles());
}
return true;
}
}
@Controller
public class SecureController {
@GetMapping("/profile")
public String userProfile(HttpServletRequest request) {
User currentUser = (User) request.getAttribute("currentUser");
if (currentUser == null) {
return "redirect:/login";
}
request.setAttribute("userProfile", userService.getProfile(currentUser.getId()));
return "profile";
}
}
7. 总结
| 特性 | 说明 |
|---|---|
| 生命周期 | 单个 HTTP 请求期间 |
| 开始时间 | 请求到达服务器时 |
| 结束时间 | 响应返回客户端后 |
| 作用域 | 当前请求和转发(forward)的请求 |
| 共享范围 | Filter → Interceptor → Controller → 视图 |
| 不共享 | 重定向(redirect)、不同请求之间 |
| 存储位置 | 服务器内存中 |
| 线程安全 | 是(每个请求在独立的线程中处理) |
关键点记住 :HttpServletRequest Attribute 是请求级别的临时存储,适合在同一请求的各个处理环节之间传递数据,但不会在多个请求之间保持状态。