Servlet与HttpServlet详解
一、Servlet概述
Servlet(Server Applet)是Java Servlet API中定义的一个接口,用于扩展Web服务器的功能。Servlet是一个运行在Web服务器或应用服务器上的Java程序,它可以动态地生成Web页面或其他类型的响应。
二、HttpServlet类
HttpServlet
类是Java Servlet API中的一个核心类,它继承自GenericServlet
类,实现了Servlet
接口,并为HTTP协议提供了特定的实现。HttpServlet
类的主要职责是处理HTTP请求和构建HTTP响应。
HttpServlet的主要方法
-
service(ServletRequest req, ServletResponse res)
这是Servlet接口中的核心方法,所有来自客户端的请求首先都会调用此方法。HttpServlet
为这个方法提供了默认实现,它会根据请求的类型(GET、POST等)调用对应的doXXX
方法。 -
doGet(HttpServletRequest request, HttpServletResponse response)
用于处理HTTP GET请求。 -
doPost(HttpServletRequest request, HttpServletResponse response)
用于处理HTTP POST请求。 -
doPut, doDelete, doOptions, doHead, doTrace 等
这些方法用于处理其他类型的HTTP请求,但在常见的Web应用中很少直接使用。 -
init(ServletConfig config)
Servlet的初始化方法,当Servlet实例被加载到内存并准备使用时,容器会调用此方法。 -
destroy()
Servlet的销毁方法,当Servlet实例不再需要并被容器卸载时,容器会调用此方法。
HttpServlet处理流程
-
部署Servlet
- 开发者编写Servlet类,继承自
HttpServlet
并覆盖相应的方法。 - 在Web应用的
web.xml
文件中配置Servlet的映射路径和初始化参数等信息。 - 将Web应用打包成WAR文件并部署到Servlet容器中(如Tomcat)。
- 开发者编写Servlet类,继承自
-
客户端请求
- 客户端(如Web浏览器)发送HTTP请求到Servlet容器。
- 请求中包含URL、HTTP方法(GET、POST等)、请求头、请求体等信息。
-
容器调度
- Servlet容器接收到请求后,解析URL路径,找到与之匹配的Servlet实例。
- 如果Servlet实例不存在,容器会创建一个。
-
调用service方法
- Servlet容器调用Servlet实例的
service
方法,并传入HttpServletRequest
和HttpServletResponse
对象。 HttpServletRequest
对象包含了请求的所有信息,如参数、头信息等。HttpServletResponse
对象用于构建并发送HTTP响应。
- Servlet容器调用Servlet实例的
-
处理请求
- 根据请求的类型(如GET、POST),
service
方法会调用相应的doXXX
方法。 - 开发者在
doXXX
方法中实现业务逻辑,如访问数据库、处理数据等。
- 根据请求的类型(如GET、POST),
-
构建响应
- 开发者使用
HttpServletResponse
对象设置响应的状态码、响应头、响应体等。 - 可以向响应体写入HTML、JSON、XML等格式的数据。
- 开发者使用
-
发送响应
- Servlet容器将
HttpServletResponse
对象封装成HTTP响应,并通过网络发送给客户端。 - 客户端接收到响应后,会显示在Web浏览器上或进行其他处理。
- Servlet容器将
-
Servlet生命周期结束
- 当Servlet实例不再需要时(如Web应用被卸载),容器会调用
destroy
方法,允许开发者进行资源清理工作。
在Servlet API中,service
方法是Servlet容器(如Tomcat)用来调用特定HTTP方法处理程序的入口点。当客户端发送一个HTTP请求到Servlet容器时,容器会解析请求,确定它是哪种类型的HTTP方法(如GET、POST、PUT、DELETE等),然后调用相应的doXXX
方法。
- 当Servlet实例不再需要时(如Web应用被卸载),容器会调用
service
方法是一个重要的方法,它根据客户端发送的HTTP请求类型(如GET、POST等)调用相应的doXXX
方法。以下是HttpServlet
类中service
方法的实现:
@Override
public void service(ServletRequest req, ServletResponse res)throws ServletException, IOException {HttpServletRequest request;HttpServletResponse response;try {// 将ServletRequest和ServletResponse强制转换为HttpServletRequest和HttpServletResponserequest = (HttpServletRequest) req;response = (HttpServletResponse) res;} catch (ClassCastException e) {throw new ServletException("Cannot cast to HttpServletRequest/HttpServletResponse");}// 获取HTTP请求方法String method = request.getMethod();if (method.equalsIgnoreCase("GET")) {// 如果是GET请求,调用doGet方法long lastModified = getLastModified(request);if (lastModified == -1) {// 标记响应不被缓存response.setDateHeader("Expires", -1);response.setHeader("Cache-Control", "no-cache");response.setHeader("Pragma", "no-cache");} else {// 检查If-Modified-Since头以判断是否发送304 Not Modified响应long ifModifiedSince = request.getDateHeader("If-Modified-Since");if (ifModifiedSince < (lastModified / 1000 * 1000)) {// 资源已修改,发送新内容maybeSetLastModified(response, lastModified);} else {// 资源未修改,发送304 Not Modified响应response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);return;}}doGet(request, response);} else if (method.equalsIgnoreCase("HEAD")) {// 如果是HEAD请求,调用doHead方法long lastModified = getLastModified(request);maybeSetLastModified(response, lastModified);doHead(request, response);} else if (method.equalsIgnoreCase("POST")) {// 如果是POST请求,调用doPost方法doPost(request, response);} else if (method.equalsIgnoreCase("PUT")) {// 如果是PUT请求,调用doPut方法doPut(request, response);} else if (method.equalsIgnoreCase("DELETE")) {// 如果是DELETE请求,调用doDelete方法doDelete(request, response);} else if (method.equalsIgnoreCase("OPTIONS")) {// 如果是OPTIONS请求,调用doOptions方法doOptions(request, response);} else if (method.equalsIgnoreCase("TRACE")) {// 如果是TRACE请求,调用doTrace方法doTrace(request, response);} else {// 对于不支持的HTTP方法,抛出ServletExceptionthrow new ServletException("HTTP method " + method + " is not supported");}
}
这个service
方法首先检查传入的ServletRequest
和ServletResponse
是否可以安全地强制转换为HttpServletRequest
和HttpServletResponse
。接着,它获取HTTP请求的方法(如GET、POST等),并根据这个方法调用相应的doXXX
方法。如果请求方法是GET,它还会检查If-Modified-Since
头以判断是否发送304 Not Modified响应。
对于不支持的HTTP方法,service
方法会抛出一个ServletException
。通常,开发者会覆盖doGet
、doPost
等方法来提供具体的业务逻辑,而service
方法则负责调度这些方法。
三、示例代码
下面是一个简单的HttpServlet
示例,展示了如何处理GET请求:
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class MyServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// 设置响应内容类型response.setContentType("text/html;charset=UTF-8");// 实际的逻辑是在这里String message = "<html><body><h1>Hello, World!</h1></body></html>";// 将响应体写入到response对象中response.getWriter().write(message);}
}
在这个示例中,MyServlet
类继承了HttpServlet
类,并重写了doGet()
方法。当容器接收到一个GET请求时,它会调用doGet()
方法来处理请求。在doGet()
方法中,我们设置了响应的内容类型为HTML,并构建了一个简单的HTML消息作为响应体。