ServletContext 是什么?它和 Servlet、Session、Request 的作用域有什么区别?
简化版
ServletContext 是「整个 Web 应用(一个 webapp)的全局上下文」——一个 Web 应用对应一个 ServletContext,它代表整个应用,所有 Servlet 共享它,生命周期和应用一样长(应用启动创建、应用停止销毁)。它有两个核心用途:① 全局共享数据(application 作用域)——用 setAttribute/getAttribute 存全应用共享的数据(所有用户、所有 Servlet 都能访问,如全局配置、统计计数);② 访问应用级信息/资源——如读取 web.xml 的初始化参数(getInitParameter)、获取应用路径(getRealPath)、加载资源(getResourceAsStream)、记录日志(log)。和其他作用域对比(作用域从大到小):ServletContext(application,整个应用/所有用户共享)> HttpSession(session,一个用户的多次请求共享)> HttpServletRequest(request,一次请求内共享)。关键区别:application 是「全应用共享」(跨用户)、session 是「一个用户会话内共享」(跨请求)、request 是「一次请求内共享」(最短)。核心:ServletContext 是应用级全局上下文,作用域最大(全应用共享),生命周期最长。
详细版
四大作用域对比:
| 作用域 | 对象 | 范围 | 生命周期 | 用途 |
|---|---|---|---|---|
| application | ServletContext | 整个应用(所有用户) | 应用启动~停止 | 全局配置、共享数据 |
| session | HttpSession | 一个用户会话 | 会话创建~过期/失效 | 用户状态(登录信息) |
| request | HttpServletRequest | 一次请求(含 forward) | 请求开始~结束 | 请求内传递数据 |
| page | pageContext(JSP) | 当前 JSP 页面 | 页面内 | 页面内(很少用) |
// ServletContext 的获取和使用
public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse resp) {
ServletContext context = getServletContext(); // 拿到 ServletContext
// ① 全局共享数据(application 作用域)
context.setAttribute("onlineCount", 100); // 全应用共享
Integer count = (Integer) context.getAttribute("onlineCount");
// ② 读 web.xml 的初始化参数
String config = context.getInitParameter("appConfig");
// ③ 获取资源/路径
String realPath = context.getRealPath("/WEB-INF/config.xml");
InputStream in = context.getResourceAsStream("/WEB-INF/data.txt");
// ④ 记录日志
context.log("something happened");
}
}
// 三个作用域存数据的对比
request.setAttribute("k", v); // 一次请求内共享
session.setAttribute("user", user); // 一个用户会话内共享(跨请求)
context.setAttribute("config", config); // 全应用共享(跨用户)
⚠️ 作用域的选择要「按数据的共享范围选最小的合适作用域」——用错作用域会导致数据泄漏或数据串。request 作用域(一次请求):适合「请求内传递数据」,如 Controller 传数据给 JSP、forward 时传参;请求结束就没了、最安全。session 作用域(一个用户会话):适合「一个用户的状态」,如登录用户信息、购物车;注意 session 是每个用户独立的(不会串),但要注意及时清理(用户量大时 session 占内存)、且默认存服务端(分布式要共享)。application 作用域(整个应用):适合「全应用共享、所有用户一样」的数据,如全局配置、字典数据、统计计数;但绝对不能把「用户相关的数据」存 application——因为 application 是所有用户共享的,你把用户 A 的信息存进去,用户 B 也能看到(数据串、越权)。所以规则是:用户相关→session、请求内→request、全局共享→application,别把用户数据存到 application。
完整版教学
一、ServletContext 是什么
先理解 ServletContext 的定位:
ServletContext(Servlet 上下文):
代表整个 Web 应用(一个 webapp)的全局上下文
一个 Web 应用 = 一个 ServletContext(全应用唯一)
生命周期:
应用启动(部署)→ 创建 ServletContext
应用停止(卸载)→ 销毁 ServletContext
→ 和应用一样长(最长的作用域)
所有 Servlet 共享一个 ServletContext:
一个应用里的所有 Servlet、Filter、Listener
→ 都能拿到同一个 ServletContext
→ 通过它共享数据、访问应用级信息
获取 ServletContext:
Servlet 里:getServletContext()
request 里:request.getServletContext()
session 里:session.getServletContext()
→ 都是同一个(全应用唯一)
ServletContext 的作用:
① 全局共享数据(application 作用域)
② 访问应用配置(web.xml 参数)、资源、路径
③ 应用级的日志、动态注册 Servlet 等
所以 ServletContext = 整个应用的全局上下文(全应用唯一、生命周期最长)
ServletContext 是「整个 Web 应用的全局上下文」——一个 Web 应用一个 ServletContext(全应用唯一)。生命周期:应用启动创建、停止销毁(和应用一样长、最长作用域)。所有 Servlet 共享一个 ServletContext(Servlet/Filter/Listener 都能拿到同一个、共享数据/访问应用信息)。获取:getServletContext()/request.getServletContext()(都是同一个)。作用:全局共享数据、访问应用配置/资源/路径、应用级日志。理解「ServletContext 整个应用的全局上下文(全应用唯一)、生命周期应用启动~停止(最长)、所有 Servlet 共享一个、作用:全局共享数据/访问应用配置资源/应用级日志」,就理解了 ServletContext 的定位。
二、四大作用域
理解 Web 应用的四大作用域(从大到小):
四大作用域(数据共享范围,从大到小):
① application(ServletContext):
范围:整个应用(所有用户、所有请求)
生命周期:应用启动~停止
用途:全应用共享的数据(全局配置、字典、统计)
→ 最大、最长
② session(HttpSession):
范围:一个用户的会话(这个用户的多次请求)
生命周期:会话创建~过期/失效(如 30 分钟不活动)
用途:一个用户的状态(登录信息、购物车)
→ 每个用户独立(不串)
③ request(HttpServletRequest):
范围:一次请求(从请求到响应,含 forward 转发)
生命周期:请求开始~结束
用途:请求内传递数据(Controller → JSP)
→ 最短、每次请求独立
④ page(pageContext,JSP 里):
范围:当前 JSP 页面
生命周期:页面处理内
→ 最小,很少用
作用域的选择原则:
数据共享范围越小越好(用最小的合适作用域)
全局共享 → application
用户状态 → session
请求内 → request
所以四大作用域:application(全应用)>session(一用户)>request(一次请求)>page
四大作用域(从大到小):① application(ServletContext,整个应用/所有用户、应用启动停止、全局共享数据)、② 过期、用户状态、每个用户独立不串)、③ session(HttpSession,一个用户会话/多次请求、会话创建request(HttpServletRequest,一次请求含 forward、请求开始~结束、请求内传递数据、最短)、④ page(pageContext,当前 JSP 页面,很少用)。选择原则:数据共享范围越小越好(全局共享用 application、用户状态用 session、请求内用 request)。理解「四大作用域从大到小:application(全应用所有用户)>session(一用户多请求,每用户独立不串)>request(一次请求含 forward 最短)>page(JSP 页面);选择越小越好:全局 application/用户 session/请求内 request」,就掌握了四大作用域。
三、application 作用域的用途
ServletContext 的核心用途之一是「application 作用域共享数据」:
application 作用域(ServletContext 的 attribute):
context.setAttribute("key", value):存全应用共享数据
context.getAttribute("key"):取
context.removeAttribute("key"):删
特点:
① 全应用共享——所有用户、所有 Servlet 都能访问同一份数据
② 生命周期最长——应用启动到停止一直存在
适合存什么(全局共享、所有用户一样的数据):
① 全局配置(从 web.xml 或配置文件读的,全应用用)
② 字典/常量数据(如省市列表,加载一次全应用用)
③ 全局统计(如在线人数、访问计数)
④ 应用级的缓存
★ 不能存什么(用户相关的数据):
用户的登录信息、用户的购物车 等用户私有数据
→ 因为 application 是所有用户共享的
→ 存进去,用户 A 的数据用户 B 也能看到 → 数据串、越权!
→ 用户相关的存 session(每个用户独立)
线程安全(重要):
application 是全应用共享的,多线程(多个请求)同时访问
→ setAttribute/getAttribute 要注意线程安全
→ 存的对象如果会被并发修改,要用线程安全的对象或加锁
所以 application 作用域用于全局共享数据(别存用户数据)
application 作用域(ServletContext 的 attribute):setAttribute/getAttribute 存全应用共享数据。特点:全应用共享(所有用户所有 Servlet 访问同一份)、生命周期最长。适合存全局配置、字典/常量、全局统计(在线人数)、应用级缓存(全局共享、所有用户一样)。不能存用户相关数据(登录信息/购物车)——application 所有用户共享、存进去用户 A 的数据 B 也能看到(数据串、越权),用户相关存 session。线程安全(重要):application 全应用共享、多线程同时访问,setAttribute/getAttribute 要注意线程安全。理解「application 作用域:setAttribute 存全应用共享数据(所有用户共享/生命周期最长);适合全局配置/字典/统计/缓存;★不能存用户数据(所有用户共享会串数据越权)、用户数据存 session;线程安全(多线程访问)」,就掌握了 application 作用域。
四、访问应用配置与资源
ServletContext 还能访问应用级的配置和资源:
① 读 web.xml 的初始化参数(context-param):
web.xml:
<context-param>
<param-name>appConfig</param-name>
<param-value>xxx</param-value>
</context-param>
context.getInitParameter("appConfig") // 读全应用的初始化参数
区分 ServletConfig 的初始化参数:
ServletConfig(getServletConfig().getInitParameter):单个 Servlet 的参数
ServletContext(getInitParameter):全应用的参数
→ 前者 servlet 级、后者应用级
② 获取路径:
context.getRealPath("/WEB-INF/config.xml") // 磁盘真实路径
context.getContextPath() // 应用的上下文路径(如 /myapp)
③ 加载资源:
context.getResourceAsStream("/WEB-INF/data.txt") // 读应用内的资源
→ 相对于应用根目录
④ 记录日志:
context.log("message") // 记录到应用的日志
⑤ 动态注册(Servlet 3.0+):
context.addServlet(...) // 动态注册 Servlet
→ 编程式配置(不用 web.xml)
所以 ServletContext 提供应用级的配置读取、资源访问、路径、日志等
ServletContext 还能访问应用级配置和资源:① 读 web.xml 初始化参数(getInitParameter,全应用的 context-param,区分 ServletConfig 的单个 Servlet 参数)、② 获取路径(getRealPath 真实路径、getContextPath 上下文路径)、③ 加载资源(getResourceAsStream 读应用内资源)、④ 记录日志(log)、⑤ 动态注册(Servlet 3.0+ addServlet 编程式配置)。理解「ServletContext 访问应用配置资源:getInitParameter 读全应用参数(区分 ServletConfig 单个 Servlet 参数)、getRealPath/getContextPath 路径、getResourceAsStream 加载资源、log 日志、addServlet 动态注册」,就掌握了访问应用配置与资源。
五、作用域的选择
理解「什么数据存什么作用域」:
按数据的共享范围选作用域(越小越好):
request 作用域(一次请求):
数据只在一次请求内用(Controller 处理后传给 JSP 显示)
forward 转发时传参(forward 在同一次请求内)
→ 请求结束就没了、最安全、最省内存
session 作用域(一个用户会话):
数据要在一个用户的多次请求间共享(跨请求)
登录用户信息、购物车、用户偏好
→ 每个用户独立、会话期间保持
注意:session 占服务端内存、分布式要共享
application 作用域(整个应用):
数据全应用共享、所有用户一样
全局配置、字典、统计计数
→ 别存用户数据(会串)
选择决策:
① 数据只在一次请求用 → request
② 数据要跨请求、但属于某个用户 → session
③ 数据全局共享、所有用户一样 → application
常见错误:
✗ 把用户数据存 application(会串、越权)
✗ 把请求内数据存 session(session 越来越大、内存泄漏风险)
→ 用最小的合适作用域
所以:请求内 request、用户级 session、全局 application
作用域的选择(按数据共享范围选最小的合适作用域):request(一次请求,Controller 传给 JSP、forward 传参,请求结束就没了最安全)、session(一个用户会话,跨请求的用户数据如登录信息/购物车,每用户独立但占内存分布式要共享)、application(整个应用,全局共享所有用户一样的配置/字典/统计,别存用户数据)。决策:只在一次请求用→request、跨请求属于某用户→session、全局共享→application。常见错误:把用户数据存 application(会串越权)、把请求内数据存 session(session 越来越大内存泄漏)。理解「作用域选择:请求内 request(最安全)/用户级 session(跨请求但占内存)/全局 application(别存用户数据);错误:用户数据存 application 会串、请求数据存 session 内存泄漏;用最小的合适作用域」,就掌握了作用域的选择。
六、实践与总结
总结 ServletContext 和作用域的实践:
ServletContext 的实践:
① 全局共享数据用 application(setAttribute),别存用户数据
② 读应用配置用 getInitParameter(区分 ServletConfig 的)
③ 访问资源用 getResourceAsStream / getRealPath
④ 注意线程安全(application 是共享的、多线程访问)
作用域的实践:
① request:请求内传数据(Controller→JSP、forward)
② session:用户状态(登录、购物车),及时清理
③ application:全局共享(配置、字典、统计),别存用户数据
现代开发的变化(Spring Boot / 前后端分离):
① 前后端分离:数据主要走 JSON,少用作用域传数据
② session 逐渐少用(JWT 无状态、REST API)
③ 全局配置用 @ConfigurationProperties(不用 ServletContext)
④ 但理解作用域仍重要(session vs request、共享范围)
核心总结:
ServletContext = 整个应用的全局上下文(全应用唯一、生命周期最长)
作用域从大到小:application > session > request > page
application 全应用共享(别存用户数据)、session 一用户、request 一次请求
按数据共享范围选最小的合适作用域
ServletContext 和作用域的实践:ServletContext(全局共享用 application 别存用户数据、读配置用 getInitParameter、访问资源用 getResourceAsStream、注意线程安全)。作用域(request 请求内传数据、session 用户状态及时清理、application 全局共享别存用户数据)。现代变化(前后端分离数据走 JSON 少用作用域、session 少用 JWT 无状态、全局配置用 @ConfigurationProperties,但理解作用域仍重要)。理解「实践:ServletContext 全局共享 application 别存用户数据+读配置+访问资源+线程安全;作用域 request 传数据/session 用户状态/application 全局;现代前后端分离少用作用域、session 少用 JWT、但理解作用域仍重要」,就掌握了实践。
记忆钩子:「ServletContext=整个 Web 应用的全局上下文(一个 webapp 一个,全应用唯一,生命周期应用启动~停止最长,所有 Servlet 共享);两大用途:①全局共享数据(application 作用域,setAttribute/getAttribute,别存用户数据否则所有用户共享会串越权)②访问应用配置资源(getInitParameter 读全应用参数/getRealPath 路径/getResourceAsStream 资源/log 日志);四大作用域从大到小:application(ServletContext,整个应用所有用户)>session(HttpSession,一个用户多次请求,每用户独立不串)>request(HttpServletRequest,一次请求含 forward,最短)>page(JSP);按数据共享范围选最小:全局 application/用户 session/请求内 request;★别把用户数据存 application(会串越权)、别把请求数据存 session(内存泄漏)」。
七、常见误区与追问
- 误区:可以把用户的登录信息存在 application(ServletContext)里。 绝对不能——application 是整个应用、所有用户共享的,把用户 A 的登录信息存进去,用户 B 也能读到(数据串、越权);用户相关的数据要存在 session(每个用户独立的会话作用域)里;application 只存全局共享、所有用户一样的数据(配置、字典、统计)。
- 误区:ServletContext 每个 Servlet 一个。 一个 Web 应用(webapp)只有一个 ServletContext(全应用唯一)——所有 Servlet、Filter、Listener 共享同一个 ServletContext;每个 Servlet 有自己的 ServletConfig(Servlet 级配置),但 ServletContext 是应用级、全局共享的。
- 误区:request 作用域能跨请求共享数据。 不能——request 作用域只在一次请求内(从请求到响应,含 forward 转发,因为 forward 还在同一次请求);请求结束数据就没了;要跨请求共享用 session(一个用户的多次请求);redirect 是两次请求,request 属性不会带过去。
- 误区:session 是每个应用一个、所有用户共享。 session 是每个用户一个、独立的——HttpSession 代表一个用户的会话,每个用户有自己的 session(通过 JSESSIONID Cookie 区分),互不干扰;application(ServletContext)才是全应用一个、所有用户共享的。
- 追问:ServletContext、HttpSession、HttpServletRequest 三个作用域有什么区别? 共享范围和生命周期不同(从大到小):ServletContext(application 作用域)代表整个应用,所有用户、所有 Servlet 共享,生命周期是应用启动到停止(最长),存全局配置、统计等;HttpSession(session 作用域)代表一个用户的会话,这个用户的多次请求共享,生命周期是会话创建到过期/失效,每个用户独立、存用户状态(登录信息、购物车);HttpServletRequest(request 作用域)代表一次请求,从请求到响应(含 forward),生命周期最短、请求结束就没了,存请求内传递的数据。
- 追问:怎么选择数据放哪个作用域? 按数据的共享范围选最小的合适作用域:数据只在一次请求内用(如 Controller 处理后传给 JSP、forward 传参)→ request;数据要在一个用户的多次请求间共享(如登录信息、购物车)→ session;数据全应用共享、所有用户一样(如全局配置、字典、统计计数)→ application;原则是「越小越好」,不要把用户相关的数据存到 application(会串、越权),也不要把只用一次的数据存到 session(占内存、有泄漏风险)。
- 追问:application 作用域用要注意什么? ① 线程安全——application 是全应用共享的,多个请求(多线程)会同时访问,setAttribute/getAttribute 存的对象如果会被并发修改,要用线程安全的对象或加锁;② 不能存用户相关数据(所有用户共享会串、越权);③ 内存——存在里面的数据整个应用生命周期都在,占内存,别存太多;④ 适合存全局共享、所有用户一样、不常变的数据(配置、字典)。
八、加强记忆
ServletContext 是「整个 Web 应用(一个 webapp)的全局上下文」——一个 Web 应用一个 ServletContext(全应用唯一),所有 Servlet 共享,生命周期和应用一样长(应用启动创建、停止销毁,最长的作用域)。两个核心用途:① 全局共享数据(application 作用域)——setAttribute/getAttribute 存全应用共享数据(别存用户数据,否则所有用户共享会串、越权);② 访问应用配置/资源——getInitParameter(读全应用参数)、getRealPath(路径)、getResourceAsStream(资源)、log(日志)。四大作用域(从大到小):application(ServletContext,整个应用/所有用户)> session(HttpSession,一个用户的多次请求,每用户独立不串)> request(HttpServletRequest,一次请求含 forward,最短)> page(JSP)。选择原则:按数据共享范围选最小的合适作用域——请求内用 request、用户状态用 session、全局共享用 application;别把用户数据存 application(会串越权)、别把请求内数据存 session(内存泄漏)。application 是全应用共享的,注意线程安全。一句话「ServletContext=整个应用的全局上下文(全应用唯一/生命周期最长/所有 Servlet 共享),用于全局共享数据(application,别存用户数据)和访问应用配置资源;作用域从大到小 application(全应用)>session(一用户不串)>request(一次请求)>page;按共享范围选最小,别把用户数据存 application(会串越权)」。