29、Java Web开发:Servlet、JSP与相关技术详解

Java Web开发:Servlet、JSP与相关技术详解

1. 浏览器与Servlet的数据交互流程

一个Servlet可以为多个用户服务,下面来回顾一下客户端与Servlet通信的整个过程。Java Servlet运行在容器中,容器会自动为每个客户端请求创建一个新线程,无需开发者进行线程编程。

1.1 通信流程

  1. 请求发起 :Web浏览器可以通过HTML表单、链接或其他程序向服务器发送Get或Post请求。当第一个用户请求访问FindBooks Servlet时,容器会检查该Servlet是否正在运行。若未运行,容器将启动它并调用其 init() 方法,即便开发者未重写此方法,它也存在于 HttpServlet 超类中。
  2. 请求处理 :容器调用Servlet超类的 service() 方法,该方法会将请求重定向到 doGet() doPost() 或类似的 doXXX() 方法,并传递 HttpServletRequest HTTPServletResponse 参数。开发者可通过 HttpServletRequest 对象的 getParameter() 方法获取用户输入的数据。
  3. 业务处理与响应返回 :获取参数后,在业务层处理数据,业务层可实现为与数据存储交互的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 实现步骤

  1. 创建HTML客户端 :创建一个类似于示例的HTML客户端,允许用户输入股票代码。
  2. 创建Servlet :创建名为 StockServerServlet 的Servlet,它接收一个参数(股票代码),并实例化 StockQuoteGenerator 类,该类的代码应类似于示例代码,但无需实现 Remote 接口。
  3. 传递参数并获取价格 :将从客户端接收到的股票代码传递给 StockQuoteGenerator ,获取价格。
  4. 返回动态HTML页面 :通过请求对象将动态创建的HTML页面返回给客户端。
  5. 测试与部署 :先在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 学习路径

  1. 基础知识学习 :首先学习Java语言的基础知识,包括面向对象编程、异常处理、输入输出等。掌握Servlet和JSP的基本概念和工作原理。
  2. 实践项目练习 :通过实际项目来巩固所学知识。可以从简单的项目开始,如实现一个简单的登录系统、商品列表展示等,逐渐增加项目的复杂度。
  3. 深入学习高级特性 :学习过滤器、事件监听器、异步Servlet等高级特性,了解它们的应用场景和实现方法。
  4. 参考优秀案例和开源项目 :参考一些优秀的Java Web开发案例和开源项目,学习他人的设计思路和代码规范。

11.2 实践注意事项

  • 代码规范 :编写代码时要遵循良好的代码规范,如命名规范、注释规范等,提高代码的可读性和可维护性。
  • 性能优化 :注意性能优化,避免出现性能瓶颈。例如,合理使用缓存、优化数据库查询等。
  • 安全问题 :重视安全问题,如防止SQL注入、XSS攻击等。使用过滤器和安全框架来增强系统的安全性。
  • 版本管理 :使用版本管理工具如Git来管理代码,方便团队协作和代码的回溯。

11.3 持续学习与交流

  • 关注技术动态 :关注Java Web开发领域的最新技术动态和发展趋势,不断学习新的知识和技能。
  • 参加技术社区和活动 :参加技术社区和线下活动,与其他开发者交流经验和心得,拓宽自己的视野。
  • 阅读技术书籍和文章 :阅读相关的技术书籍和文章,深入学习和理解各种技术的原理和应用。

总之,Java Web开发中的Servlet、JSP及相关技术是构建高效、稳定Web应用程序的重要工具。开发者应深入理解这些技术的原理和应用场景,根据具体需求选择合适的技术,并不断学习和实践,以跟上技术发展的步伐。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值