Servlet三大作用域对象怎么用

Servlet 里的作用域对象是个绕不开的概念,request、session、application 三个篮子各有各的生命周期和可见范围。这篇把三个作用域对象的用法、差异和选择时机都讲清楚,附带一个经典的转发和重定向的坑。

学 Servlet 的时候,有个绕不过去的概念:作用域对象。

说白了就是几个「存东西的篮子」,数据放进哪个篮子,决定了它能被谁看见、能活多久。

当年我也被 request、session、application 这几个搞混过,什么时候用哪个、放进去的东西什么时候没,一度靠蒙。后来理清了作用域的「大小」关系,才算真正会用。这篇就把这几个内置的作用域对象讲明白。

作用域是啥意思

作用域(Scope),通俗讲就是「数据的有效范围」。

你往某个作用域对象里塞一个值,这个值能被哪些请求、哪些用户、能存活多长时间,由这个作用域的「级别」决定。

Servlet 里最核心的作用域对象有三个,按范围从小到大排:

  1. request(请求级)
  2. session(会话级)
  3. application(应用级)

它们的 API 几乎一模一样,都是 setAttribute(名, 值) 存、getAttribute(名) 取、removeAttribute(名) 删。

区别只在于「存了之后能活多久、谁能看到」。这才是重点。

request 请求作用域

范围最小,也最常用。

数据只在一次请求内有效。请求处理完,响应发回去,里面的东西就没了。

典型场景:Servlet 处理完业务,把结果塞进 request,转发给 JSP 去展示。

// Servlet 里:把查到的用户放进 request
protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    // 查出用户
    User user = userService.findById(1001);
    // 存进 request 作用域
    request.setAttribute("user", user);
    // 转发到 JSP,注意是 forward 不是 redirect
    request.getRequestDispatcher("/user.jsp").forward(request, response);
}
​
<%-- JSP 里:从 request 取出来用 --%>
<p>用户名:${requestScope.user.name}</p>
​

这里有个关键点:必须是转发(forward),不能是重定向(redirect)。

转发是服务器内部跳转,request 对象传递下去,数据还在。

重定向是让浏览器重新发一次请求,原来那个 request 没了,数据自然也没了。

这是新手最容易踩的坑,数据明明存进去了,JSP 里就是取不到,八成是用了 redirect。

session 会话作用域

范围比 request 大,数据在一个用户的整个会话期间有效。

只要用户不关浏览器、不超时、不主动退出,session 里的数据就一直在。

典型场景:登录后把用户信息放进 session,后续请求都能取到,不用每次查数据库。

// 登录成功后存 session
protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    String username = request.getParameter("username");
    String password = request.getParameter("password");

    User user = userService.login(username, password);
    if (user != null) {
        // 存进 session
        HttpSession session = request.getSession();
        session.setAttribute("loginUser", user);
        response.sendRedirect("/home");
    } else {
        response.sendRedirect("/login?error=1");
    }
}
​
// 其他 Servlet 里取 session 数据
HttpSession session = request.getSession(false); // false:不存在就返回 null,不新建
if (session != null) {
    User loginUser = (User) session.getAttribute("loginUser");
    // ...
}
​

getSession() 和 getSession(false) 的区别要注意:

  • getSession() / getSession(true):没有 session 就新建一个
  • getSession(false):没有 session 就返回 null

判断用户是否登录时要用 getSession(false),不然每个请求都会创建一个空 session,浪费资源。

session 默认超时时间是 30 分钟,可以在 web.xml 里改:

<session-config>
    <!-- 单位:分钟 -->
    <session-timeout>60</session-timeout>
</session-config>
​

application 应用作用域

范围最大,数据在整个应用运行期间有效,所有用户、所有请求都能看到。

application 对象就是 ServletContext,Tomcat 启动时创建,关闭时销毁。

典型场景:全局配置、全站计数器、所有用户共享的缓存数据。

// 往 application 存数据
ServletContext application = getServletContext();
application.setAttribute("siteConfig", config);

// 取数据(任何 Servlet/JSP 里都能取到)
ServletContext application = request.getServletContext();
SiteConfig config = (SiteConfig) application.getAttribute("siteConfig");
​

用 application 作用域要特别注意线程安全问题。

多个请求可能同时读写同一个数据,如果是可变对象(比如 Map、List),要加锁或者用线程安全的实现。

三个作用域对比

request session application
生命周期 一次请求 用户会话期间 应用启动到关闭
可见范围 当前请求链路 同一用户的所有请求 所有用户的所有请求
典型用途 转发数据到视图 登录状态、购物车 全局配置、共享缓存
线程安全 单线程(安全) 单用户(相对安全) 多线程(需要注意)

怎么选

选作用域的原则就一句话:够用就好,不要用比需要更大的作用域。

  • 只是当前请求处理完要传给视图用?用 request。
  • 需要跨多个请求、同一个用户共享?用 session。
  • 需要所有用户都能看到、整个应用共享?用 application。

很多人图省事啥都往 session 里塞,结果 session 越来越大,内存撑不住。request 够用的场景坚决用 request。

小结

Servlet 三大作用域对象本质上就是三个不同生命周期的 Map:

  • request:一次请求,转发可用,重定向不行
  • session:一个用户的会话,getSession(false) 判登录状态
  • application:全站共享,注意线程安全

记住「范围从小用起」这个原则,基本不会踩坑。

更多推荐

章节目录