我最开始写的是SpringMVC的开始案例,从SpringMvc的请求原理,写着写着,就写到了Servlet的生命周期了,感觉越写越底层啊。主要掌握Servlet的生命周期,以及HttpServler与Servlet的关系
类关系结构
Servlet
Servlet这个类是一个接口,并不是一个实体类,但是这个接口可以说,就是祖师爷了,所有的Servlet服务都实现了它。
servlet的生命周期,就是init ->service ->destroy,可以说就是挺简单的,在JDK中的,Servlet的实现类是GenericServlet
GenericServlet
GenericServlet 这个类是一个抽象类,并没有完全的实现Servlet的所有接口,service 这个方法为抽象方法,由子类去实现(这种设计模式被成为模板模式(钩子函数))
service方法是抽象方法,大家可以仔细的看我框出来的那个函数,前面有一个 A
,表示的是抽象方法
HttpServlet
HttpServlet实现了GenericServlet类,但是仍然是抽象类,需要子类去继承HttpServlet,然后复写里面的doGet和doPost方法
下图的代码可以看到,虽然是实现了Servlet所有接口,但是并不能直接使用,需要子类去继承HttpServlet
service方法与doGet和doPost关系
类/接口 | 关系 |
---|---|
javax.servlet.Servlet(接口) | 定义的接口 |
javax.servlet.GenericServlet(抽象类) | 实现Servlet |
javax.servlet.http.HttpServlet(抽象类) | 继承了GenericServlet抽象类 |
在下代码,大家可以发现,所谓的doGet和doPost方法,都不过是一个钩子函数,大家最后调用的都是service方法,在方法里面判断请求的类型,然后再去调用钩子函数doGet/doPost
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String method = req.getMethod();
//请求的方法是否是get类型
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 = req.getDateHeader(HEADER_IFMODSINCE);
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);
//是否是post类型
} 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);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1]; errArgs[0] = method; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg); } }
ServletRequest/HttpServletRequest
在HttpServlet中的service方法,会将接口定义的ServletRequest 转化为HttpServletRequest,然后调用自己复写的service方法来处理请求。
大家看图会发现,其实HttpServletRequest接口继承了ServletRequest接口和HttpServletResponse接口继承了ServletResponse接口
说实话有人真的很无尿,会问你,HttpServletRequest和ServletRequest的区别,我尼玛想呼死你,这两个接口的关系是继承关系,区别就是HttpServletRequest(子类)拥有更多的http相关处理的方法咯
生命周期
Servlet 生命周期:Servlet 加载—>实例化—>服务—>销毁。
方法 | 执行次数 | 执行时候 |
---|---|---|
init | 一次 | 1、客户端调用Servlet,2、容器初始化,在web.xml配置load-on-startup,3、容器初始化,配置了init-param |
service | 多次 | 每次客户端访问servlet,都会调用 |
destroy | 一次 | 当容器退出时(Tomcat退出) |
init(只执行一次,在):在Servlet的生命周期中,仅执行一次init()方法。它是在服务器装入Servlet时执行的,负责初始化Servlet对象。可以配置服务器,以在启动服务器或客户机首次访问Servlet时装入Servlet。无论有多少客户机访问Servlet,都不会重复执行init。
service(执行多次):它是Servlet的核心,负责响应客户的请求。每当一个客户请求一个HttpServlet对象,该对象的Service()方法就要调用,而且传递给这个方法一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象作为参数。在HttpServlet中已存在Service()方法。默认的服务功能是调用与HTTP请求的方法相应的然后去调用doGet和doPost等。
destroy(执行一次,在容器退出(Tomcat关闭)): 仅执行一次,在服务器端停止且卸载Servlet时执行该方法。当Servlet对象退出生命周期时,负责释放占用的资源。一个Servlet在运行service()方法时可能会产生其他的线程,因此需要确认在调用destroy()方法时,这些线程已经终止或完成。
创建Servlet对象的时机
1、Servlet容器启动时:读取web.xml配置文件中的信息,构造指定的Servlet对象,创建ServletConfig对象,同时将ServletConfig对象作为参数来调用Servlet对象的init方法。
2、在Servlet容器启动后:客户首次向Servlet发出请求,Servlet容器会判断内存中是否存在指定的Servlet对象,如果没有则创建它,然后根据客户的请求创建HttpRequest、HttpResponse对象,从而调用Servlet 对象的service方法。
3、Servlet Servlet容器在启动时自动创建Servlet,这是由在web.xml文件中为Servlet设置的属性决定的。从中我们也能看到同一个类型的Servlet对象在Servlet容器中以单例的形式存在。
<servlet>
<servlet-name>Init</servlet-name>
<servlet-class>com.yellowcong.servlet.InitServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>