多Servlet共享变量实现计数器:变量定义与未初始化读取问题
简易计数器Servlet共享变量问题
需求说明
创建一个简易计数器应用,包含三个不同的Servlet:
- 读取数值:
ReadValue,处理GET请求 - 递增数值:
Increment,处理POST请求 - 递减数值:
Decrement,处理POST请求
原代码示例
ReadValue.java
@WebServlet("/readValue") public class ReadValue extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().println("Value is " + value); } }
Increment.java
@WebServlet("/incrementValue") public class Increment extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { value++; response.getWriter().println("Now value is " + value); } }
Decrement.java
@WebServlet("/decrementValue") public class Decrement extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if (value > 0) { value--; response.getWriter().println("Now value is " + value); } } }
问题
- 变量
value应该定义在何处? - 了解
ServletContext可用于多个Servlet间共享变量,但如果ServletContext未被设置时就调用读取它的Servlet,会发生什么?
解答
1. 变量value的定义位置
变量value需要放在所有Servlet都能访问的共享存储容器中,最合适的就是ServletContext(你提到的ServerContext是笔误,正确名称是ServletContext)。
原因:每个Servlet是单例实例,但三个Servlet是不同的类实例,若把value定义为各自的成员变量,三个Servlet会维护独立的数值,完全达不到共享计数的效果。而ServletContext是Web应用的全局上下文,整个应用内所有Servlet都可以访问其中的属性,完美适配多Servlet共享变量的场景。
2. 未设置ServletContext变量时调用读取Servlet的后果
如果直接调用读取Servlet,而ServletContext中还没存入value,getAttribute("counterValue")会返回null。此时若直接使用这个null值(比如拼接字符串、做自增/自减运算),会触发NullPointerException空指针异常,导致请求报错。
优化后的代码实现
方式1:在每个Servlet中判断并初始化
ReadValue.java(读取)
@WebServlet("/readValue") public class ReadValue extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletContext context = getServletContext(); // 检查变量是否存在,不存在则初始化为0 Integer value = (Integer) context.getAttribute("counterValue"); if (value == null) { value = 0; context.setAttribute("counterValue", value); } response.getWriter().println("Value is " + value); } }
Increment.java(递增)
@WebServlet("/incrementValue") public class Increment extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletContext context = getServletContext(); Integer value = (Integer) context.getAttribute("counterValue"); if (value == null) { value = 0; } value++; context.setAttribute("counterValue", value); response.getWriter().println("Now value is " + value); } }
Decrement.java(递减)
@WebServlet("/decrementValue") public class Decrement extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletContext context = getServletContext(); Integer value = (Integer) context.getAttribute("counterValue"); if (value == null) { value = 0; } if (value > 0) { value--; context.setAttribute("counterValue", value); } response.getWriter().println("Now value is " + value); } }
方式2:用监听器全局初始化(更优雅)
创建ServletContextListener,在Web应用启动时就初始化计数器值,避免每个Servlet都做判断:
@WebListener public class CounterInitListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { // 应用启动时初始化计数器为0 ServletContext context = sce.getServletContext(); context.setAttribute("counterValue", 0); } @Override public void contextDestroyed(ServletContextEvent sce) { // 应用销毁时可选择保存计数器值到持久化存储(可选) } }
这种方式下,所有Servlet都可以直接从ServletContext获取counterValue,无需再做null判断。
内容的提问来源于stack exchange,提问作者bui3
相关产品推荐
相关产品推荐

