JavaWeb开发是构建企业级后端服务的基石,而Servlet与JSP则是这门技术的核心支柱。虽然现代开发中Spring Boot等框架大行其道,但直接基于Servlet和JSP进行开发能够帮助开发者彻底弄清HTTP请求的处理流程以及MVC设计模式的底层演变逻辑。掌握这两项基础技术,对于理解各类高级框架的内部原理有着不可替代的作用。

理解Servlet与JSP的核心职责与底层原理
在JavaWeb体系中,Servlet扮演着控制器的角色,它主要负责接收客户端发来的HTTP请求,调用后端的业务逻辑进行处理,并根据处理结果决定响应方式。Servlet遵循严格的生命周期:类加载、实例化、初始化(调用init()方法)、处理请求(调用service()方法,进而根据HTTP方法类型调用doGet()或doPost())以及销毁(调用destroy()方法)。这种生命周期使得Servlet实例一旦创建便能常驻内存,以多线程方式处理后续请求,具备极高的运行效率。
与Servlet不同,JSP(JavaServer Pages)的初衷是为了简化视图层的开发。如果在Servlet中通过拼接HTML字符串来输出页面,代码将极其臃肿且难以维护。JSP允许开发者在HTML页面中嵌入Java代码(使用<% %>脚本)和表达式(使用<%= %>),从而更直观地生成动态内容。事实上,JSP在运行前会被容器(如Tomcat)编译成一个Servlet类。也就是说,JSP本质上就是一种特殊的Servlet,它只是把视图渲染的繁重工作封装了起来,最终执行的依然是Java字节码。
构建JavaWeb项目并配置核心路由
要让Servlet能够正确响应特定的URL请求,必须进行路由映射配置。传统的配置方式是在项目的WEB-INF目录下的web.xml文件中进行声明。这种方式虽然繁琐,但集中管理了所有路由信息,在复杂项目中有利于全局把控。在web.xml中,需要先声明Servlet的名称和全限定类名,再通过映射标签将该Servlet绑定到具体的URL模式上。
<!-- web.xml 传统配置方式 -->
<servlet>
<servlet-name>UserServlet</servlet-name>
<servlet-class>com.example.web.UserServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UserServlet</servlet-name>
<url-pattern>/user/query</url-pattern>
</servlet-mapping>
随着Servlet 3.0规范的发布,注解配置成为了更主流的选择。开发者只需在Servlet类上使用@WebServlet注解,直接指定URL模式即可,这极大减少了XML配置的冗余代码,提升了开发效率。现代的Tomcat服务器默认支持这种基于注解的配置方式,使得项目结构更加清爽。
// 使用注解配置Servlet路由
@WebServlet("/user/query")
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 处理GET请求逻辑
resp.getWriter().write("Query User Success");
}
}
实战演练:基于MVC模式实现数据查询与展示
真正规范的JavaWeb应用应当遵循MVC架构模式,将模型、视图和控制器分离。下面通过一个用户信息查询的案例来演示JSP与Servlet的协同工作。首先,我们定义一个普通的Java类User作为数据模型,它包含姓名和年龄属性。接着,在UserServlet的doGet方法中,我们模拟从数据库获取数据,将数据绑定到请求域中,并将请求转发给JSP页面进行渲染。
// UserServlet.java 充当Controller角色
@WebServlet("/user/detail")
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 模拟业务逻辑,创建Model数据
User user = new User();
user.setName("张三");
user.setAge(28);
// 将数据存入request作用域
req.setAttribute("userInfo", user);
// 请求转发到JSP视图
req.getRequestDispatcher("/WEB-INF/views/userDetail.jsp").forward(req, resp);
}
}
在视图层,JSP页面接收到转发过来的请求后,从请求域中提取数据并展示。为了使JSP页面更加纯净,避免嵌入难看的Java脚本,我们强烈建议使用EL表达式和JSTL标签库。EL表达式以${}的简洁形式取代了<%= %>,而JSTL则提供了循环、条件判断等控制逻辑的标签化实现。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<title>用户详情</title>
</head>
<body>
<h1>用户信息</h1>
<p>姓名:${userInfo.name}</p>
<p>年龄:${userInfo.age}</p>
</body>
</html>
页面跳转机制:请求转发与重定向的深度对比
在Web开发中,页面跳转是极其频繁的操作,主要分为请求转发和重定向两种方式,它们的底层机制和适用场景截然不同。请求转发是服务器端的行为,当Servlet调用forward()方法时,Web容器会将当前的请求对象和响应对象传递给目标资源,浏览器的地址栏不会发生变化。因为是同一个请求,所以转发前Servlet中设置的请求域属性在目标JSP中依然有效。这种方式常用于携带数据跳转到视图页面。
重定向则是客户端的行为。当Servlet调用sendRedirect()方法时,服务器会向浏览器返回一个302状态码以及新的Location地址,浏览器收到后会自动发起一次对新地址的请求。因此,浏览器的地址栏会更新为新的URL,且这是两次完全独立的HTTP请求,第一次请求中绑定的数据会在第二次请求中丢失。如果需要在跳转后保留数据,必须借助Session或重写URL参数。重定向通常用于表单提交后的页面跳转,以防止用户刷新页面导致表单重复提交。