HttpServletRequest中的 Attribute(属性)生命周期和作用域是 Java Web 开发中的重要概念

以下是详细的解释:

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 是​​请求级别​​的临时存储,适合在同一请求的各个处理环节之间传递数据,但不会在多个请求之间保持状态。

相关推荐
m0_495562782 小时前
Swift-static和class
java·服务器·swift
合作小小程序员小小店2 小时前
web网页开发,在线物流管理系统,基于Idea,html,css,jQuery,jsp,java,SSM,mysql
java·前端·后端·spring·intellij-idea·web
这周也會开心3 小时前
SpringMVC整理
java·springmvc
東雪木3 小时前
Spring Boot 2.x 集成 Knife4j (OpenAPI 3) 完整操作指南
java·spring boot·后端·swagger·knife4j·java异常处理
数学难3 小时前
Java面试题2:Java线程池原理
java·开发语言
Charles_go3 小时前
C#8、有哪些访问修饰符
java·前端·c#
qwer12321ck763 小时前
srcType instanceof Class 及泛型 vs 普通类
java
咸鱼求放生3 小时前
Java 8 Stream API
java·开发语言
moiumxf0278q4 小时前
C++中智能指针是如何工作的?
java·jvm·c++