Servlet是JavaEE提供给我们来处理TCP连接,解析HTTP协议这些底层工作的,我们使用Servlet API编写自己的Servlet来处理HTTP请求,Web服务器实现Servlet API接口,实现底层功能,我们只需要把自己的应用程序跑在Web
服务器上。
一个Servlet总是继承自HttpServlet,然后覆写doGet()或doPost()方法。但是不管是doGet还是doPost,它们传入的参数都是HttpServletRequest和HttpServletResponse两个对象,分别代表HTTP请求和响应。这两个对象为我们封装好了请求和响应,我们可以直接使用它们来执行相应的操作即可。
而这样的一个项目最终会打包成一个*.war
文件,想要运行这个文件,就需要使用支持Servlet
API
的Web容器(Web服务器)。
因此,我们首先要找一个支持Servlet API的Web服务器。常用的服务器有:
- Tomcat:由Apache开发的开源免费服务器;
- Jetty:由Eclipse开发的开源免费服务器;
- GlassFish:一个开源的全功能JavaEE服务器。
这里我们选择使用最广泛的开源免费的Tomcat
服务器。Tomcat服务器实际上就是一个提供Servlet运行的容器,在这个容器中运行的Servlet有以下特点:
- 无法在代码中直接通过
new
创建Servlet
实例,必须由Servlet
容器自动创建Servlet
实例; Servlet
容器只会给每个Servlet
类创建唯一实例;Servlet
容器会使用多线程执行doGet()
或doPost()
方法。
一个Web App就是由一个或多个Servlet
组成的,每个Servlet
通过注解说明自己能处理的路径。一个Webapp
完全可以有多个Servlet
,分别映射不同的路径。
注意:早期的Servlet
需要在web.xml
中配置映射路径,但最新Servlet
版本只需要通过注解就可以完成映射。
客户端浏览器在通过一个URL
路径发起对一个Servlet
请求的过程中,其本质是在调用执行Servlet
实例的doXXX()
方法。该Servlet
实例创建和使用的过程,被称为Servlet的生命周期。整个生命周期共有四个部分,分别是:实例化、初始化、服务、销毁。
- 实例化:根据
Servlet
请求的路径,Tomcat服务器会查找该Servlet
的实例。如果该实例不存在,则通过调用无参构造方法,完成Servlet
实例的创建,该实例只会在第一次请求该Servlet时创建,在没有销毁此实例之前,所有对该Servlet的请求都会反复使用此实例,也就是说,Servlet实例只会在第一次请求的时候创建。
- 初始化:通过该
Servlet
的实例,调用init()
方法,执行初始化的逻辑,我们如果要在执行Servlet之前进行一些操作,都可以在初始化时完成。 - 服务:通过该
Servlet
的实例,调用service()
方法,如果子类没有重写该方法,则调用HttpServlet父类的service()
方法,在父类的该方法中进行请求方式的判断,如果是GET
请求,则调用doGet()
方法,若子类没有重写该方法,则会调用父类的doGet()方法,而父类的doGet()方法只会报出一个405
状态码的错误页面;如果是POST
请求,则调用doPost()
方法,和doGet()方法一样,若子类没有重写该方法,父类的doPost()方法也会报出一个405
状态码的错误页面。
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
// Invalid date header - proceed as if none was set
ifModifiedSince = -1;
}
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
405状态码:代表请求的方式服务器不提供支持。
底层代码如下:
HttpServlet的doGet()方法:
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String msg = lStrings.getString("http.method_get_not_supported");
sendMethodNotAllowed(req, resp, msg);
}
HttpServlet的doPost()方法:
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String msg = lStrings.getString("http.method_post_not_supported");
sendMethodNotAllowed(req, resp, msg);
}
4.销毁:服务器关闭或重启时,会销毁所有的Servlet实例,会调用Servlet实例的destroy()
方法,如果你想在销毁Servlet实例时做一些操作,就可以重写父类HttpServlet的destroy()
方法,否则,Servlet实例会调用父类的destroy()
方法。
Servlet生命周期的具体流程如下图所示: