JSP+Servlet制作简易计算器
'# JSP+Servlet制作简易计算器
一、背景与问题
在Web开发早期,JSP(Java Server Pages)和Servlet是构建动态网站的核心技术。它们通过分离业务逻辑与展示层,实现了MVC架构的雏形。本文将以简易计算器为例,深入解析JSP与Servlet的协作机制,并探讨其在实际项目中的适用场景。
一个典型的问题场景是:用户在Web页面输入两个数字和运算符,需要在服务器端完成计算并返回结果。这种需求虽然简单,却能体现JSP和Servlet的典型工作流程。
二、基本原理
JSP与Servlet的协作流程可分为三个阶段:
- 请求处理:用户通过浏览器发送HTTP请求,Servlet接收请求并解析参数
- 业务处理:Servlet执行计算逻辑,可能涉及数据校验、业务规则
- 响应生成:Servlet将结果传递给JSP页面,JSP生成HTML响应返回给浏览器
关键点在于:Servlet负责处理业务逻辑,JSP负责页面展示,二者通过request对象进行数据传递。
三、环境准备
开发环境建议:
- JDK 1.8+
- Apache Tomcat 9.x
- IDE:IntelliJ IDEA 或 Eclipse
项目结构建议:
webapps/ └── calculator ├── WEB-INF/ │ └── web.xml ├── index.jsp └── CalculatorServlet.java
四、核心实现
1. JSP页面:用户交互界面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>简易计算器</title>
</head>
<body>
<h2>简易计算器</h2>
<form action="calculator" method="post">
数字1: <input type="text" name="num1"><br>
运算符:
<select name="operator">
<option value="+">+</option>
<option value="-">-</option>
<option value="*">*</option>
<option value="/">/</option>
</select><br>
数字2: <input type="text" name="num2"><br>
<input type="submit" value="计算">
</form>
<%
// 显示计算结果
if (request.getAttribute("result") != null) {
out.println("<h3>结果: " + request.getAttribute("result") + "</h3>");
}
%>
</body>
</html>关键点分析:
- 使用
<form>标签定义表单提交路径 - 通过
<select>实现运算符选择 - 使用JSP EL表达式显示计算结果
2. Servlet处理逻辑
@WebServlet("/calculator")
public class CalculatorServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取参数
String num1Str = request.getParameter("num1");
String num2Str = request.getParameter("num2");
String operator = request.getParameter("operator");
// 数据校验
if (num1Str == null || num2Str == null || operator == null) {
request.setAttribute("error", "缺少必要参数");
request.getRequestDispatcher("index.jsp").forward(request, response);
return;
}
try {
double num1 = Double.parseDouble(num1Str);
double num2 = Double.parseDouble(num2Str);
double result = 0;
switch (operator) {
case "+": result = num1 + num2; break;
case "-": result = num1 - num2; break;
case "*": result = num1 * num2; break;
case "/":
if (num2 == 0) {
request.setAttribute("error", "除数不能为零");
request.getRequestDispatcher("index.jsp").forward(request, response);
return;
}
result = num1 / num2;
break;
default:
request.setAttribute("error", "无效的运算符");
request.getRequestDispatcher("index.jsp").forward(request, response);
return;
}
request.setAttribute("result", result);
request.getRequestDispatcher("index.jsp").forward(request, response);
} catch (NumberFormatException e) {
request.setAttribute("error", "请输入有效的数字");
request.getRequestDispatcher("index.jsp").forward(request, response);
}
}
}关键点分析:
- 使用
@WebServlet注解定义Servlet映射 - 实现完整的参数校验逻辑
- 使用
switch处理不同运算符 - 异常处理机制
- 使用
RequestDispatcher实现页面跳转
3. 配置文件:web.xml
<web-app>
<servlet>
<servlet-name>CalculatorServlet</servlet-name>
<servlet-class>CalculatorServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CalculatorServlet</servlet-name>
<url-pattern>/calculator</url-pattern>
</servlet-mapping>
</web-app>五、完整案例
完整项目结构如下:
webapps/
└── calculator
├── index.jsp
├── WEB-INF/
│ └── web.xml
└── CalculatorServlet.java运行步骤:
- 部署到Tomcat服务器
- 访问
http://localhost:8080/calculator/index.jsp - 输入数字和运算符后点击"计算"按钮
- 查看计算结果
完整代码示例(Servlet部分):
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
@WebServlet("/calculator")
public class CalculatorServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String num1Str = request.getParameter("num1");
String num2Str = request.getParameter("num2");
String operator = request.getParameter("operator");
if (num1Str == null || num2Str == null || operator == null) {
request.setAttribute("error", "缺少必要参数");
request.getRequestDispatcher("index.jsp").forward(request, response);
return;
}
try {
double num1 = Double.parseDouble(num1Str);
double num2 = Double.parseDouble(num2Str);
double result = 0;
switch (operator) {
case "+": result = num1 + num2; break;
case "-": result = num1 - num2; break;
case "*": result = num1 * num2; break;
case "/":
if (num2 == 0) {
request.setAttribute("error", "除数不能为零");
request.getRequestDispatcher("index.jsp").forward(request, response);
return;
}
result = num1 / num2;
break;
default:
request.setAttribute("error", "无效的运算符");
request.getRequestDispatcher("index.jsp").forward(request, response);
return;
}
request.setAttribute("result", result);
request.getRequestDispatcher("index.jsp").forward(request, response);
} catch (NumberFormatException e) {
request.setAttribute("error", "请输入有效的数字");
request.getRequestDispatcher("index.jsp").forward(request, response);
}
}
}六、源码解析
- Servlet生命周期:当请求到达时,Servlet容器会创建Servlet实例并调用
doPost方法 - 参数获取机制:通过
request.getParameter()获取表单提交的数据 - 异常处理:包含多个异常处理分支,包括空值检查、类型转换异常和运算符校验
- 结果传递:使用
request.setAttribute()将结果传递给JSP页面 - 页面跳转:通过
RequestDispatcher实现前后端数据传递
七、进阶使用
1. 历史记录功能
// 在Servlet中添加记录逻辑
List<String> history = (List<String>) getServletContext().getAttribute("history");
if (history == null) {
history = new ArrayList<>();
}
history.add(num1Str + operator + num2Str + "=" + result);
getServletContext().setAttribute("history", history);2. 输入验证增强
// 增加正则表达式校验
if (!num1Str.matches("^-?\\d+(\\.\\d+)?$")) {
request.setAttribute("error", "请输入有效的数字");
request.getRequestDispatcher("index.jsp").forward(request, response);
return;
}3. 使用JSTL标签
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
...
<c:if test="${not empty history}">
<h3>历史记录:</h3>
<ul>
<c:forEach items="${history}" var="item">
<li>${item}</li>
</c:forEach>
</ul>
</c:if>八、性能与工程实践
1. 性能优化
- 缓存计算结果:对于重复计算可使用缓存机制
- 异步处理:对于复杂计算可使用线程池
- 减少HTTP请求:将部分逻辑移到前端JavaScript处理
2. 安全风险
- XSS攻击:用户输入未转义可能导致脚本注入
- CSRF攻击:未验证请求来源可能导致恶意提交
- SQL注入:虽然本例未涉及数据库,但需注意参数化查询
3. 安全改进
// 使用JSTL的fmt标签转义输出
<fmt:formatNumber value="${result}" type="number" />九、常见问题与踩坑
1. 常见错误示例
// 错误示例:直接使用out.println()可能导致乱码
out.println("结果: " + result);问题分析:未设置正确的字符编码,可能导致中文乱码
解决办法:
<%@ page contentType="text/html;charset=UTF-8" %>2. 运算符处理错误
// 错误示例:未处理除法中的除零情况
result = num1 / num2;问题分析:直接除法可能导致运行时异常
解决办法:增加条件判断
3. 未处理空值
// 错误示例:未检查参数是否为空
double num1 = Double.parseDouble(num1Str);问题分析:可能导致NumberFormatException
解决办法:增加空值检查
十、最佳实践
- 分层架构:将业务逻辑与展示层分离
- 输入验证:在多个层进行校验(前端+后端)
- 异常处理:统一处理异常,避免直接暴露错误信息
- 日志记录:记录关键操作日志,便于调试
- 资源管理:及时关闭资源,避免内存泄漏
十一、总结
JSP+Servlet的组合虽然已逐渐被现代框架取代,但在理解Web开发基础原理方面仍具有重要价值。通过简易计算器的实现,我们深入解析了:
- HTTP请求的处理流程
- JSP与Servlet的协作机制
- 业务逻辑与展示层的分离
- 异常处理与安全防护
- 实际开发中的常见问题
在实际项目中,这种方案适用于:
- 简单的页面交互需求
- 无需复杂业务逻辑的场景
- 需要快速搭建的原型系统
但不推荐用于:
- 高并发场景
- 复杂业务系统
- 需要高安全性的应用
随着技术发展,建议采用Spring MVC、React+Node.js等现代技术栈,以获得更好的开发效率和系统扩展性。
评论已关闭