servlet 通常被称为服务器端小程序,是运行在服务器端的程序,用于处理及相应客户端的请求。
Servlet 的生命周期
当 Servlet 在 Web 容器中运行时,其实例的创建及销毁等都不是由我们程序员决定的,而是由 Web 容器进行控制的。
创建 Servlet 实例有两个时机:
:① 客户端第一次请求某个 Servlet 时系统创建该 Servlet 实例(大部分都是该方式)。
② Web 应用启动时立即创建 Servlet 实例, 即设定 load-on-startup,该属性接收一个整型值,该值越小优先级越高。
Servlet 生命周期可被定义为从创建到毁灭的整个过程:
① Servlet 通过调用 init() 方法进行初始化。
② Servlet 调用 service() 方法来处理客户端的请求。
③ Servlet 通过调用 destroy() 方法终止。
④ 最后,Servlet 被 JVM 垃圾回收器回收。
1、init() 方法
init() 在 Servlet 的整个生命周期中被设计成只调用一次,第一次创建 Servlet 时被调用,后续每次用户请求不再调用。它是在服务器装入 Servlet 时执行,当用户调用一个 Servlet 时,就会创建一个 Servlet 实例,每一个用户请求都会产生一个新的线程,适当的时候移交 doGet 或 doPost 方法。 init() 方法简单的创建或加载一些数据,这些数据将被用于 Servlet 的整个生命周期。通常无需重写该方法,除非在初始化 Servlet 时要完成某些资源的初始化操作。
如果重写了 init(ServletConfig config) 方法,则应在重写该方法的第一行调用 super.init(config)。该方法将调用 HttpServlet 的 init 方法。
public void init() throws ServletException {
super.init();
System.out.println("init......");
}
public void init(ServletConfig config) throws ServletException {
super.init(config);
System.out.println("TestServlet init......");
}
2、service() 方法
service() 是执行实际任务的主要方法,是 Servlet 的核心,该方法用于生成对客户端的响应。 Servlet 容器(即 Web 容器)调用 service() 方法来处理来自客户端(浏览器)的请求,并把格式化的响应写回给客户端。
service() 方法检查 HTTP 请求类型(GET,POST, PUT, DELETE 等),默认的服务功能是调用与 HTTP 请求的方法相对应的 doGet, doPost, doPut, doDelete 等方法。
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.service(req, resp);
}
service() 方法由容器调用,我们不用对 service() 方法做任何的动作,只需要根据请求类型来重载 doGet() 或 doPost() 就行。
3、doGet() 和 doPost() 方法
我们可以根据请求的类型在该方法体内部实现我们的代码。
4、destroy() 方法
destroy() 方法只会被调用一次,即 Servlet 生命周期结束时被调用,即在服务器停止且卸载 Servlet 时执行该方法,通常无需重写该方法。destroy() 方法可以用来释放占用的资源,如数据库连接关闭、停止后台线程、把 Cookie 列表或点击计数器写入到磁盘,并执行其他类似的清理活动。
destroy() 执行之后, Servlet 对象被标记为垃圾回收。
@Override
public void destroy() {
super.destroy();
}
5、Servlet 生命流程的大致流程图
Servlet 的工作原理
1、Servlet 解析客户端 http 请求流程图
步骤:
① web 客户向 Servlet 容器发出 HTTP 请求。
② Servlet 容器解析 web 的 HTTP 请求。
③ Servlet 容器创建一个 HttpServletRequest 对象,在这个对象中封装了 HTTP 请求信息。
④ Servlet 容器创建一个 HttpServletResponse 对象。
⑤ Servlet 容器调用 HttpServlet 的 service() 方法,把 HttpRequest 和 HttpResponse 对象作为 service() 方法的参数传给 HttpServlet 对象。
⑥ HttpServlet 调用 HttpRequest 的相关方法获取 HTTP 请求信息。
⑦ HttpServlet 调用 HttpResponse 的相关方法生成相应数据。
⑧ Servlet 容器把 HttpServlet 的响应结果返回 web 用户。
2、工作原理解析:
① 接收和响应客户请求:客户端先向 Servlet 容器发送一个请求,Servlet 调用 service() 方法进行响应,service() 方法对请求的方式进行匹配,选择相应的 do 方法,然后再进入对应的方法中调用逻辑层的方法,实现对客户端的响应。源码如下:
if(method.equals("GET")) {
errMsg = this.getLastModified(req);
if(errMsg == -1L) {
this.doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader("If-Modified-Since");
if(ifModifiedSince < errMsg) {
this.maybeSetLastModified(resp, errMsg);
this.doGet(req, resp);
} else {
resp.setStatus(304);
}
}
} else if(method.equals("HEAD")) {
errMsg = this.getLastModified(req);
this.maybeSetLastModified(resp, errMsg);
this.doHead(req, resp);
} else if(method.equals("POST")) {
this.doPost(req, resp);
} else if(method.equals("PUT")) {
this.doPut(req, resp);
} else if(method.equals("DELETE")) {
this.doDelete(req, resp);
} else if(method.equals("OPTIONS")) {
this.doOptions(req, resp);
} else if(method.equals("TRACE")) {
this.doTrace(req, resp);
} else {
String errMsg1 = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[]{method};
errMsg1 = MessageFormat.format(errMsg1, errArgs);
resp.sendError(501, errMsg1);
}
② GenericServlet 是一个通用的,不特定于任何协议的 Servlet ,它实现了 Servlet 接口, 而 HttpServlet 继承于 GenericServlet,因此 HttpServlet 也实现了 Servlet 接口,我们在自定义 Servlet 接口时只需要继承 HttpServlet 接口就行。
③ Servlet 接口和 GenericServlet 是不特定于任何协议的, 而 HttpServlet 是特定于 HTTP 协议的类, 所以HttpServlet中实现了service()方法,并将请求ServletRequest、ServletResponse 强转为HttpRequest 和 HttpResponse。HttpServlet 中的源码如下:
if(req instanceof HttpServletRequest && res instanceof HttpServletResponse) {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
this.service(request, response);
} else {
throw new ServletException("non-HTTP request or response");
}