Servlet¶
约 990 个字 73 行代码 预计阅读时间 6 分钟
为了简化Java EE
的开发,Servlet
应运而生。在 JavaEE 平台上,处理 TCP 连接,解析 HTTP 协议这些底层工作由 Web 服务器完成,Servlet 则是运行在 Web 服务器上的程序,负责处理 HTTP 请求,生成 HTTP 响应。Servlet 通过 Servlet API 与 Web 服务器进行交互。
Servlet 通常情况下与使用 CGI(Common Gateway Interface,公共网关接口)实现的程序有异曲同工的效果,但 Servlet 具有很多优势:
- 性能显著优于 CGI。
- Servlet 在 Web 服务器的地址空间内执行,不需要单独创建进程处理每个请求。
- Servlet 是独立于平台的,因为它们是用 Java 编写的。
- 服务器上的 Java 安全管理器执行了一系列限制,以保护服务器计算机上的资源。因此,Servlet 是可信的。
- Java 类库的全部功能对 Servlet 来说都是可用的。它可以通过 sockets 和 RMI 机制与 applets、数据库或其他软件进行交互。
block-beta
columns 3
space:2 A["Myservlet"]
space:2 B["Servlet API"]
Browser blockArrowId<["HTTP"]>(x) C["Web Server"]
Servlet 基础 ¶
通常情况下定义 Servlet 需要继承javax.servlet.http.HttpServlet
类并重写doXXX
( 如doGet、doPost
) 方法或者service
方法,重写HttpServlet
类的service
方法可以获取到上述七种 Http 请求方法的请求。也可以继承GenericServlet
或者自己实现Servlet
接口。
javax.servlet.http.HttpServlet类继承于javax.servlet.GenericServlet
,而GenericServlet
又实现了javax.servlet.Servlet
和javax.servlet.ServletConfig
。
javax.servlet.Servlet
接口中只定义了servlet
基础生命周期方法:init(初始化)
、getServletConfig(配置)
、service(服务)
、destroy(销毁)
, 而HttpServlet
不仅实现了servlet
的生命周期并通过封装service
方法抽象出了doGet/doPost/doDelete/doHead/doPut/doOptions/doTrace
方法用于处理来自客户端的不一样的请求方式。
package cc.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
// WebServlet注解表示这是一个Servlet,并映射到地址/
@WebServlet(urlPatterns = "/")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
PrintWriter pw = resp.getWriter();
pw.write("<h1>Hello, world!(Servlet Test)</h1>");
pw.flush();
}
}
http://localhost:8080/
即可看到Hello, world!(Servlet Test)
。

Servlet 的打包类型为 war,表示 Java Web Application Archive。普通的 java 程序通过启动 JVM,执行 jar 包中的 main 方法,而 war 则需要部署到 Web 服务器中,由 Web 服务器加载。常用的支持 Servlet API 的 Web 服务器有 Tomcat、Jetty、WebLogic、WebSphere 等。
一个 Webapp 可以有多个 Servlet,分别映射不同的路径。例如:
@WebServlet(urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {
...
}
@WebServlet(urlPatterns = "/signin")
public class SignInServlet extends HttpServlet {
...
}
@WebServlet(urlPatterns = "/")
public class IndexServlet extends HttpServlet {
...
}
浏览器会根据路由访问不同的 Servlet,这种功能被称为 dispatch。值得注意的是,\
实际匹配所有路径\*
。
多线程问题
一个 Servlet 类在服务器中只有一个实例,但对于每个 HTTP 请求,Web 服务器会使用多线程执行请求。因此,一个 Servlet 的 doGet()、doPost() 等处理请求的方法是多线程并发执行的。如果 Servlet 中定义了字段,要注意多线程并发访问的问题
重定向与转发 ¶
Redirect
Servlet 可以通过 HttpServletResponse#sendRedirect()
发送 302 重定向,使浏览器重新请求一个新的 URL。
// 将 /hi 重定向到 /hello
@WebServlet(urlPatterns = "/hi")
public class RedirectServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 构造重定向的路径:
String name = req.getParameter("name");
String redirectToUrl = "/hello" + (name == null ? "" : "?name=" + name);
// 发送重定向响应:
resp.sendRedirect(redirectToUrl);
}
}
如果要实现 301 永久重定向,可以使用HttpServletResponse#setStatus()
方法设置状态码为 301。
Forwward
Forward 指内部转发,即 Servlet 在处理请求时,将请求转发给另一个 Servlet 处理。
@WebServlet(urlPatterns = "/morning")
public class ForwardServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getRequestDispatcher("/hello").forward(req, resp);
}
}
Session 和 Cookie ¶
对于 Web 应用程序来说,我们总是通过 HttpSession 这个高级接口访问当前 Session。可以认为 Web 服务器在内存中自动维护了一个 ID 到 HttpSession 的映射表,我们可以用下图表示:
Servlet 容器会在第一次调用request.getSession()
时自动创建一个 SessionID,服务器将其通过JSSESIONID
这个 Cookie 发送给浏览器。浏览器在后续的请求中会自动发送这个 Cookie,服务器就可以根据这个 Cookie 找到对应的 Session。
除了上述提到的 JSESSIONID 外,还可以通过getCookies()
Filter¶
JavaEE 的 Servlet 规范提供了 Filter 组件,即过滤器,它的作用是,在 HTTP 请求到达 Servlet 之前,可以被一个或多个 Filter 预处理,类似打印日志、登录检查等逻辑。
Example
@WebFilter("/user/*")
public class AuthFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("AuthFilter: check authentication");
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
if (req.getSession().getAttribute("user") == null) {
// 未登录,自动跳转到登录页:
System.out.println("AuthFilter: not signin!");
resp.sendRedirect("/signin");
} else {
// 已登录,继续处理:
chain.doFilter(request, response);
}
}
}
Servlet 规范并没有对 @WebFilter 注解标注的 Filter 规定顺序。如果一定要给每个 Filter 指定顺序,就必须在 web.xml 文件中对这些 Filter 进行配置
创建日期: 2024年8月16日 12:44:21