Java Web开发:Servlet、JSP与相关技术详解
1. 浏览器与Servlet的数据交互流程
一个Servlet可以为多个用户服务,下面来回顾一下客户端与Servlet通信的整个过程。Java Servlet运行在容器中,容器会自动为每个客户端请求创建一个新线程,无需开发者进行线程编程。
1.1 通信流程
- 请求发起 :Web浏览器可以通过HTML表单、链接或其他程序向服务器发送Get或Post请求。当第一个用户请求访问FindBooks Servlet时,容器会检查该Servlet是否正在运行。若未运行,容器将启动它并调用其
init()方法,即便开发者未重写此方法,它也存在于HttpServlet超类中。 - 请求处理 :容器调用Servlet超类的
service()方法,该方法会将请求重定向到doGet()、doPost()或类似的doXXX()方法,并传递HttpServletRequest和HTTPServletResponse参数。开发者可通过HttpServletRequest对象的getParameter()方法获取用户输入的数据。 - 业务处理与响应返回 :获取参数后,在业务层处理数据,业务层可实现为与数据存储交互的POJO或EJB。通过获取
PrintWriter对象的引用将结果返回给客户端,该对象可向用户发送文本数据。对于非文本结果,可使用OutputStream类。同时,别忘了调用setContentType()方法设置内容类型。
1.2 示例代码
以下代码用于获取用户输入的书名,并返回价格为65美元的响应:
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
String title = req.getParameter("booktitle");
PrintWriter out = res.getWriter();
res.setContentType("text/html");
out.println("<html><body>");
out.println("<h2>the book "+title+" costs only $65");
out.println("<p>Please enter your credit card number");
out.println("</body></html>");
}
1.3 流程图
graph TD;
A[用户请求] --> B{Servlet是否运行};
B -- 否 --> C[启动Servlet并调用init()];
B -- 是 --> D[调用service()];
C --> D;
D --> E[重定向到doGet()/doPost()];
E --> F[获取参数];
F --> G[业务处理];
G --> H[返回响应];
2. HTTP的Get和Post请求
HTTP规范定义了多种Web数据交换方法,其中Get和Post最为常用。若未指定方法,默认使用Get。
2.1 Get请求
当HTML表单使用 method=Get 时,容器将调用Servlet的 doGet() 方法。此时,Web浏览器会将表单中输入的值追加到URL末尾的问号后面。例如,用户输入书名“Apollo”,URL可能如下:
http://www.mybooks.com?booktitle=Apollo
若表单提交多个值,URL将包含多个键值对。Get请求的优点是便于复制、粘贴或收藏带参数的URL,但缺点是数据未受保护,以明文形式显示。
2.2 Post请求
Post请求通常用于向服务器发送大量数据,也用于发送二进制数据。登录表单应使用Post请求,以避免用户的ID和密码显示在URL中。为处理Post请求,Servlet必须重写 doPost() 方法。
2.3 使用场景
一般来说,Get用于数据检索,Post用于数据检索和修改。
2.4 对比表格
| 请求方法 | 数据显示 | 数据量 | 安全性 | 使用场景 |
|---|---|---|---|---|
| Get | 明文显示在URL | 有限 | 低 | 数据检索 |
| Post | 不显示在URL | 无限制 | 高 | 数据检索与修改、发送二进制数据 |
3. 会话跟踪
HTTP是无状态协议,用户从FindBooks Servlet或其他服务器端程序获取包含书籍列表的网页后,再访问其他网页时,新页面并不知道前一页面显示的内容。为在多个网页间保留数据,需实现会话跟踪。
3.1 会话的概念
会话是用户访问网站时试图完成的逻辑任务。例如,买书过程可能包括选书、输入账单和运输信息等步骤,这些步骤共同构成一个会话。下单后,会话结束。
3.2 创建会话
可通过调用 request.getSession(true) 方法创建会话。调用该方法时,若未找到客户端的会话对象,则会创建一个新对象。
3.3 会话数据存储方式
会话信息可存储在客户端或服务器端。
- 客户端存储 :可使用Cookie或URL重写。
- Cookie :容器发送给Web客户端的小块数据,存储在磁盘上。每次客户端发起请求时,Cookie会返回给服务器,将请求与会话唯一关联。例如,创建并发送Cookie的代码如下:
Cookie myCookie = new Cookie("bookName","Java Programming 24-hour trainer");
myCookie.setMaxAge(60*60*24);
response.addCookie(myCookie);
- **URL重写**:若客户端禁用Cookie,可使用URL重写进行会话跟踪。此时,会话ID和其他所需会话数据会附加到URL字符串中。
- 服务器端存储 :使用实现
javax.servlet.http.HTTPSession接口的会话跟踪API。例如,创建或查找会话对象的代码如下:
HttpSession mySession = request.getSession(true);
3.4 会话管理示例
以下代码展示了在Servlet的 doGet() 方法中创建会话和购物车的过程:
HttpSession session = request.getSession(true);
Vector myShoppingCart = session.getAttribute("shoppingCart");
if (myShoppingCart == null) {
myShoppingCart = new Vector();
}
Book selectedBook = new Book();
selectedBook.title = request.getParameter("booktitle");
selectedBook.price = Double.parseDouble(request.getParameter("price"));
myShoppingCart.addElement(selectedBook);
session.setAttribute("shoppingCart", myShoppingCart);
当订单下达后,可调用 session.invalidate() 方法关闭会话。若未显式使会话无效,应用服务器将在特定时间段后自动执行此操作,该时间段称为超时时间,是可配置的参数。
4. 过滤器
即使Servlet部署后,也可在不修改其代码的情况下改变其处理请求和响应的方式。可创建过滤器,它是可配置的代码,能在请求传递到Servlet之前或Servlet返回请求后处理HTTP请求。
4.1 过滤器的作用
过滤器适用于添加认证、日志记录、加密、数据压缩和审计报告等功能,这些操作不依赖于Servlet实现的业务逻辑。若需要对多个Servlet应用相同的功能,创建一个过滤器并应用于所有Servlet比在每个Servlet中重复编写代码更高效。若需要进行压缩和加密,可编写两个过滤器并将它们链接起来。
4.2 创建过滤器
要创建过滤器,需编写一个实现 javax.servlet.Filter 接口并使用 @WebFilter 注解的类。该接口包含三个方法: doFilter() 、 init() 和 destroy() 。以下是一个与两个Servlet(FindBooks和RemoveBook)一起使用的过滤器示例:
@WebFilter(servletNames={"/FindBooks", "/RemoveBook"})
public class MyAuthenticationFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 用户认证代码在此处
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void destroy() {}
}
容器会将请求和响应对象传递给过滤器。开发者可检查客户端传递的参数(如检查ID/密码),执行认证操作。若用户无效,可调用 response.getWriter() 并向用户发送消息,而无需将控制权传递给Servlet。 destroy() 方法在容器移除过滤器之前调用,若过滤器创建了一些资源(如数据库连接),应在该方法中关闭它们。 init() 方法在过滤器实例化时仅调用一次,过滤器会获取 FilterConfig 对象,通过该对象可访问Servlet上下文和初始化参数。
4.3 过滤器工作流程
graph TD;
A[客户端请求] --> B[过滤器];
B -- 通过 --> C[Servlet];
B -- 不通过 --> D[返回错误信息];
C --> E[过滤器];
E --> F[客户端响应];
5. 事件监听器
Servlet生命周期中会发生多个重要事件,开发者可在自定义事件监听器类中编写代码对这些事件做出反应。
5.1 常见事件监听器接口
- ServletContextListener :用于处理Servlet上下文创建事件。
- HttpSessionListener :用于捕获会话创建、无效或超时的时刻。
- ServletRequestListener :用于捕获Servlet请求处理开始的时刻。
5.2 示例代码
以下是一个 HttpSession 监听器示例,用于记录每个用户会话的创建和销毁:
import javax.servlet.http.HttpSessionListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSession;
import javax.servlet.ServletConfig;
import javax.servlet.annotation.WebListener;
@WebListener
public class BookStoreSessionListener implements HttpSessionListener {
public void init(ServletConfig config) {}
public void sessionCreated(HttpSessionEvent event) {
HttpSession session = event.getSession();
System.out.println("BookStore session created; id:" + session.getId());
}
public void sessionDestroyed(HttpSessionEvent event) {
HttpSession session = event.getSession();
System.out.println("BookStore session destroyed; id:" + session.getId());
}
}
为测试该监听器,可修改FindBooks Servlet的 doGet() 方法来创建和销毁会话:
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
request.getSession(true);
PrintWriter out = response.getWriter();
out.println("<html><body bgcolor=yellow>");
out.println("<h2>Hello from FindBooks</h2>");
request.getSession(true).invalidate();
}
通过从Eclipse IDE和Web浏览器分别向FindBooks Servlet发送请求,可模拟多用户场景,在运行服务器的Eclipse控制台视图中可看到不同ID的会话创建和销毁信息。
6. 异步Servlet
Servlet会自动为每个用户请求创建并分配一个单独的线程,但每个线程会占用系统资源(包括内存和CPU周期)。当并发请求达到一定数量时,服务器将停止响应。例如,数千个用户同时访问FindBooks Servlet,每个请求都需要进行三秒的数据库搜索,在这三秒内,容器会锁定每个线程,仅等待数据库查询结果。
6.1 异步Servlet的概念
异步Servlet的理念是尽量减少线程锁定时间。若用户A的请求在数据库服务器上需要三秒处理,容器的线程将分配给用户B的请求。当用户A的结果从数据库返回时,容器会分配该线程(或其他线程)将结果返回给用户A。这种架构可显著增加同一服务器可处理的并发请求数量。
6.2 历史实现方式
过去,Servlet容器供应商提供了各自的异步处理专有解决方案。例如,Jetty通过“continuations”概念实现线程重用,即请求的线程在业务逻辑完成所需的时间内暂停,结果准备好后再恢复。Apache Tomcat和GlassFish基于事件模型“Comet”实现技术,通过维护与Servlet容器的HTTP连接,服务器通过该连接将数据推送给客户端。
6.3 Servlet 3.0标准实现
Servlet 3.0规范引入了基于Comet的标准方式来创建异步Java EE Web应用程序。在 doGet() 或 doPost() 方法中,可实例化 javax.servlet.AsyncContext 对象,该对象会创建一个异步工作线程,在保留客户端请求和响应对象的同时,不会锁定客户端线程。异步处理超出了本教程的范围,但可在 此处 找到异步Servlet编程的详细示例。
7. 实践项目:股票价格查询
7.1 项目要求
编写一个简单的HTML客户端,包含一个文本输入框和一个提交按钮。用户输入股票代码,程序生成随机报价并返回包含报价的网页。可复用相关代码生成价格报价。
7.2 实现步骤
- 创建HTML客户端 :创建一个类似于示例的HTML客户端,允许用户输入股票代码。
- 创建Servlet :创建名为
StockServerServlet的Servlet,它接收一个参数(股票代码),并实例化StockQuoteGenerator类,该类的代码应类似于示例代码,但无需实现Remote接口。 - 传递参数并获取价格 :将从客户端接收到的股票代码传递给
StockQuoteGenerator,获取价格。 - 返回动态HTML页面 :通过请求对象将动态创建的HTML页面返回给客户端。
- 测试与部署 :先在Eclipse IDE中测试Servlet,然后创建WAR文件并部署到GlassFish Server的文档根目录。启动GlassFish Server,在Web浏览器中打开步骤1中创建的HTML文件,输入不同的股票代码测试Servlet。
8. JavaServer Pages(JSP)
8.1 JSP的概念
JSP是Servlet发展的下一步。JSP 2.2是Java EE 6规范的一部分,使用JSP可完成Servlet能做的所有事情,且更轻松。
8.2 示例对比
假设已创建并部署一个显示“Hello World”的Servlet,其代码如下:
out.println("<html><body>Hello World </body></html>");
若需要修改该HTML页面的布局(如在顶部添加几行空行),Java开发者可修改代码、重新编译并重新部署Servlet。但对于基于HTML的UI小修改,使用熟悉HTML的Web设计师更为高效。此时,JSP就非常有用。可让设计师创建 HelloWorld.jsp 文件:
<html>
<body>
Hello World
</body>
</html>
将该文件放置在Servlet容器的文档根目录(如MyBooks.com)中,用户可通过在Web浏览器中输入以下URL访问该JSP:
http://www.MyBooks.com/HelloWorld.jsp
首次请求该页面时,JSP容器(Servlet容器供应商也支持JSP)将根据 HelloWorld.jsp 文件的内容自动生成、编译并部署一个Servlet。后续对该JSP的调用将处理得更快,因为 HelloWorld Servlet已部署、加载到内存并运行。实际上,JSP和Servlet都可预加载,以确保即使是第一个用户的请求也能快速响应。
8.3 JSP优势总结
| 对比项 | Servlet | JSP |
|---|---|---|
| 代码编写 | 主要是Java代码,处理HTML较繁琐 | 可直接嵌入HTML,便于前端修改 |
| 修改成本 | 修改HTML布局需重新编译部署 | 前端人员可直接修改,无需重新编译 |
| 开发效率 | 适合复杂业务逻辑处理 | 适合快速开发和前端调整 |
综上所述,Servlet、JSP及相关技术在Java Web开发中各有优势,开发者可根据具体需求选择合适的技术来构建高效、稳定的Web应用程序。
9. 总结与技术选择建议
9.1 各项技术总结
| 技术名称 | 主要功能 | 适用场景 | 优势 | 劣势 |
|---|---|---|---|---|
| Servlet | 处理客户端请求,与服务器交互,执行业务逻辑 | 处理复杂业务逻辑,如数据库操作、用户认证等 | 性能高,可控制请求和响应的细节 | 编写HTML代码繁琐,前端修改成本高 |
| JSP | 动态生成HTML页面,结合Java代码和HTML | 快速开发Web页面,前端修改频繁的场景 | 可直接嵌入HTML,便于前端人员修改 | 若Java代码过多,页面可读性差 |
| 过滤器 | 在请求到达Servlet之前或响应返回客户端之前进行预处理和后处理 | 添加认证、日志记录、加密等通用功能 | 可复用代码,减少Servlet代码冗余 | 配置和调试相对复杂 |
| 事件监听器 | 监听Servlet生命周期中的重要事件并做出响应 | 统计用户会话信息、监控系统状态等 | 可实现系统的动态监控和管理 | 需要额外编写监听器类,增加代码量 |
| 异步Servlet | 减少线程锁定时间,提高服务器并发处理能力 | 处理大量并发请求的场景 | 显著提高服务器性能 | 开发和调试难度较大 |
| 会话跟踪(Cookie、URL重写、HttpSession) | 在多个页面间保留用户会话数据 | 用户购物、登录等需要保持状态的场景 | 确保用户操作的连贯性和数据的一致性 | Cookie可能被用户禁用,URL重写会使URL变长 |
9.2 技术选择建议
- 业务逻辑复杂 :若应用程序需要处理大量的业务逻辑,如数据库查询、复杂计算等,应优先选择Servlet。Servlet可以更好地控制请求和响应的处理过程,保证系统的性能和稳定性。
- 前端修改频繁 :对于前端页面布局和内容经常变化的项目,JSP是更好的选择。前端人员可以直接修改JSP文件中的HTML代码,而无需重新编译和部署整个应用程序。
- 通用功能添加 :当需要为多个Servlet添加相同的功能,如认证、日志记录等,使用过滤器可以避免代码的重复编写,提高代码的可维护性。
- 系统监控和管理 :若需要对系统的运行状态进行监控和管理,如统计用户会话信息、记录系统事件等,可使用事件监听器。
- 高并发场景 :在处理大量并发请求的情况下,异步Servlet可以显著提高服务器的性能和响应能力。
- 状态管理 :对于需要在多个页面间保留用户状态的应用,会话跟踪技术(Cookie、URL重写、HttpSession)是必不可少的。应根据具体情况选择合适的会话跟踪方式,如用户可能禁用Cookie时,可考虑使用URL重写。
9.3 技术应用流程总结
graph LR;
A[客户端请求] --> B{选择技术};
B -- 业务逻辑复杂 --> C[Servlet];
B -- 前端修改频繁 --> D[JSP];
B -- 通用功能添加 --> E[过滤器];
B -- 系统监控 --> F[事件监听器];
B -- 高并发 --> G[异步Servlet];
B -- 状态管理 --> H[会话跟踪];
C --> I[处理请求,执行业务逻辑];
D --> J[生成动态HTML页面];
E --> K[预处理和后处理];
F --> L[监听事件并响应];
G --> M[减少线程锁定,提高并发];
H --> N[保留用户会话数据];
I --> O[返回响应];
J --> O;
K --> O;
L --> O;
M --> O;
N --> O;
O --> P[客户端响应];
10. 未来技术发展趋势展望
10.1 微服务架构与Servlet、JSP的结合
随着微服务架构的兴起,将Servlet和JSP应用拆分为多个小型、自治的服务成为可能。每个微服务可以专注于特定的业务功能,通过轻量级的通信机制进行交互。这样可以提高系统的可扩展性、灵活性和可维护性。例如,将一个大型的电子商务应用拆分为商品服务、订单服务、用户服务等多个微服务,每个服务可以使用Servlet或JSP来实现具体的业务逻辑。
10.2 响应式编程与异步Servlet
响应式编程是一种处理异步数据流的编程范式,它可以更好地处理高并发、异步操作。将响应式编程与异步Servlet结合,可以进一步提高服务器的性能和响应能力。例如,使用Reactor、RxJava等响应式编程库来处理异步请求,实现非阻塞的I/O操作。
10.3 前端框架与JSP的融合
现代前端框架如Vue.js、React.js等提供了强大的组件化开发能力和良好的用户体验。将这些前端框架与JSP结合,可以实现前后端分离的开发模式。前端负责页面的展示和交互,后端使用JSP或Servlet提供数据接口。这样可以提高开发效率,同时让前端和后端团队可以并行开发。
10.4 容器化与Servlet、JSP应用部署
容器化技术如Docker和Kubernetes可以将Servlet和JSP应用打包成独立的容器,实现快速部署和资源隔离。通过容器化,可以简化应用的部署过程,提高系统的可靠性和可移植性。例如,将一个Servlet应用打包成Docker镜像,然后使用Kubernetes进行集群管理和自动伸缩。
11. 学习与实践建议
11.1 学习路径
- 基础知识学习 :首先学习Java语言的基础知识,包括面向对象编程、异常处理、输入输出等。掌握Servlet和JSP的基本概念和工作原理。
- 实践项目练习 :通过实际项目来巩固所学知识。可以从简单的项目开始,如实现一个简单的登录系统、商品列表展示等,逐渐增加项目的复杂度。
- 深入学习高级特性 :学习过滤器、事件监听器、异步Servlet等高级特性,了解它们的应用场景和实现方法。
- 参考优秀案例和开源项目 :参考一些优秀的Java Web开发案例和开源项目,学习他人的设计思路和代码规范。
11.2 实践注意事项
- 代码规范 :编写代码时要遵循良好的代码规范,如命名规范、注释规范等,提高代码的可读性和可维护性。
- 性能优化 :注意性能优化,避免出现性能瓶颈。例如,合理使用缓存、优化数据库查询等。
- 安全问题 :重视安全问题,如防止SQL注入、XSS攻击等。使用过滤器和安全框架来增强系统的安全性。
- 版本管理 :使用版本管理工具如Git来管理代码,方便团队协作和代码的回溯。
11.3 持续学习与交流
- 关注技术动态 :关注Java Web开发领域的最新技术动态和发展趋势,不断学习新的知识和技能。
- 参加技术社区和活动 :参加技术社区和线下活动,与其他开发者交流经验和心得,拓宽自己的视野。
- 阅读技术书籍和文章 :阅读相关的技术书籍和文章,深入学习和理解各种技术的原理和应用。
总之,Java Web开发中的Servlet、JSP及相关技术是构建高效、稳定Web应用程序的重要工具。开发者应深入理解这些技术的原理和应用场景,根据具体需求选择合适的技术,并不断学习和实践,以跟上技术发展的步伐。
超级会员免费看

被折叠的 条评论
为什么被折叠?



