1、Servlet的生命周期
Servlet 生命周期指的是 Servlet 对象从创建到销毁的整个过程,包括以下阶段:
-
加载和实例化阶段(loading and instantiation):当 Servlet 容器启动或者第一次请求某个 Servlet 时,会加载并创建 Servlet 对象的实例。在此阶段,容器会调用 ServletContext 的 getServlet 方法来获取 Servlet 实例,并调用其 init 方法进行初始化。在 init 方法中,Servlet 可以进行一些初始化工作,如加载配置文件、建立数据库连接等。
-
就绪阶段(ready):当 Servlet 初始化完成后,容器会将其放入就绪状态,表示它已经准备好处理客户端请求了。
-
请求处理阶段(request handling):当客户端发起请求时,Servlet 容器会为每个请求创建一个新的线程,并调用 Servlet 的 service 方法处理请求。在 service 方法中,Servlet 可以读取请求数据、进行业务处理,并生成响应数据发送给客户端。
-
销毁阶段(destruction):当 Servlet 容器关闭或者 Web 应用程序被卸载时,会调用 Servlet 的 destroy 方法,此时 Servlet 会执行一些清理工作,如关闭数据库连接、保存会话数据等。在销毁阶段结束后,Servlet 实例将被销毁并释放资源。
需要注意的是,Servlet 生命周期中 init 和 destroy 方法只会在 Servlet 实例创建和销毁时被调用一次,而 service 方法则会在每个请求到达时被调用一次。此外,Servlet 还可以实现其他生命周期方法,如 init(ServletConfig config)、getServletConfig() 等,以提供更加灵活的初始化和配置方式。
一般情况下,Servlet只有在容器关闭时才会被销毁,但也可以通过Servlet的destroy()方法手动销毁Servlet。当Servlet不再被需要时,可以通过调用destroy()方法来释放资源、关闭数据库连接、取消注册等操作。Servlet的生命周期是整个应用程序中Servlet的初始化、请求处理和销毁的过程。
Servlet 生命周期流程如下图所示:
下面通过一个案例加深对 Servlet 生命周期的理解:
@WebServlet("/myServletLife")
public class MyServletLife extends HttpServlet {
private static final long serialVersionUID = 1L;
private int initCount = 0;
private int httpCount = 0;
private int destoryCount = 0;
@Override
public void destroy() {
destoryCount++;
super.destroy();
// 向控制台输出destory方法被调用次数
System.out.println(
"**********************************destroy方法:" + destoryCount + "*******************************");
}
@Override
public void init() throws ServletException {
initCount++;
super.init();
// 向控制台输出init方法被调用次数
System.out.println("调用init方法:" + initCount);
}
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
httpCount++;
// 控制台输出doGet方法次数
System.out.println("doGet方法:" + httpCount);
// 设置返回页面格式与字符集
resp.setContentType("text/html;charset=UTF-8");
PrintWriter writer = resp.getWriter();
// 向页面输出
writer.write("初始化次数:" + initCount + "<br/>" + "处理请求次数:" + httpCount + "<br/>" + "销毁次数:" + destoryCount);
writer.close();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}
启动 Tomcat,在地址栏输入“localhost:8080/web/myServletLife”,多次访问 MyServlet,结果如下图。
关闭 Tomcat 服务器,控制台输出如下图
2、Spring 中一个 Controller 是一个Servlet 吗?
在 Spring 中,每个 Controller 类并不是一个 Servlet,而是通过 Servlet 映射器(HandlerMapping)和适配器(HandlerAdapter)来将 HTTP 请求映射到相应的方法进行处理。
具体来说,Spring MVC 框架使用了 DispatcherServlet 作为前置控制器,负责接收客户端的请求并将请求分发给相应的 Controller 进行处理。DispatcherServlet 会根据配置的 Servlet 映射器和适配器来选择合适的 Controller 类,并将请求转发给该类中的具体方法进行处理。在方法中,可以通过注解来定义请求参数、返回值和异常处理等信息,从而更加方便地实现业务逻辑的编写。
因此,虽然每个 Controller 类并不是一个 Servlet,但在 Spring MVC 框架中,它们可以像 Servlet 一样接收并处理 HTTP 请求。同时,Spring 还提供了很多方便的注解和工具类来简化 Controller 类的编写,使得开发者可以更加专注于业务逻辑的实现。
// Controller 只是 springmvc 中的处理器,通过 Servlet 映射器去匹配
3、Spring MVC逻辑
Spring的MVC框架围绕DispatcherServlet设计,Spring MVC 通过一个前端的servlet接受请求,并将这些工作委托给其他组件进行处理。Spring MVC会维护一个默认组件列表
处理流程
-
客户端发出的一个HTTP请求,WEB应用服务器接受到这个请求,如果匹配DispatcherServlet的请求映射路径(web.xml中指定),则Web容器将该请求转交给DispatcherServlet处理
-
接受请求后,DispatcherServlet会迭代上下文中所有的HandlerMapping,直到某个HandlerMapping返回HandlerExecutionChain为止,如:BeanNameUrlHandlerMapping会用URL匹配beanName,找到则封装一个HandlerExecutionChain实例返回。
HandlerExecutionChain实例封装了一个handler处理对象和一些interceptors,HandlerAdapter使用handler处理对象调用最终的控制器,如:BeanNameUrlHandlerMapping返回的HandlerExecutionChain实例中,handler处理对象为bean的name。
-
DispatcherServlet迭代上下文中所有的HandlerAdapter,将handler对象作为参数传给HandlerServlet通过调用HnadlerAdapter实例的handle方法进行控制器的调用,调用该方法时,会传递HandlerExecutionChain实例里的handler处理对象,该方法返回ModelAndView
-
HandlerAdapter在调用handler时,使用HandlerMethodArgumentResolver进行参数解析,不同的参数类别由不同的HandlerMethodArgumentResolver实例处理。
在进行参数解析时,如果参数是引用类型则需要DataBinder的协助,DataBinder可以完成请求数据到引用类型参数的绑定,以及数据的格式化及数据验证,数据类型转换也由DataBinder完成。
如果参数要获取所有请求体(如:@ResponseBody),则需要对应的消息转换器
-
如果调用过程中出现异常,则会迭代HandlerExceptionResolver,由对应的异常处理器返回ModelAndView
-
从返回的ModelAndview中获取视图名称,然后从Spring上下文中获取视图解析器,通过视图解析器处理视图,生成View
-
当得到真实的视图对象View后,DispatcherServlet就使用这个View对象对ModelAndView中的模型数据进行视图渲染
-
最终客户端得到的响应信息可能是一个普通的HTML页面,也可能是一个XML或者JSON串,甚至是一个图片或者哟个PDF等不同的媒体形式。
4、ssm启动
-
Tomcat启动时会调用实现了ServletContainerInitializer接口的类,
-
SpringServletContainerInitializer会调用实现了WebApplicationInitializer接口的类,
-
AbstractAnnotationConfigDispatcherServletInitializer实现了WebApplicationInitializer。
-
AbstractAnnotationConfigDispatcherServletInitializer中配置DispatcherServlet,但是没配置拦截路径,并且会创建Spring容器,但是没有配置类,所以得提供配置类
5、在 Spring 中有多少个 Servlet ?
在一个 Spring 应用程序中,通常会有一个或多个 Servlet。其中,最常见的是 DispatcherServlet,它是 Spring MVC 框架的核心组件之一,负责接收客户端请求并将请求分发给相应的 Controller 进行处理。
除了 DispatcherServlet,Spring 还提供了一些其他的 Servlet,包括:
ContextLoaderServlet:用于在 Web 应用程序启动时加载 Spring 配置文件,并将 ApplicationContext 对象存储在 ServletContext 中供其他组件使用。
FrameworkServlet:是 DispatcherServlet 的父类,用于处理与 Spring 框架相关的 Servlet 请求,如请求映射、视图解析等。
HttpPutFormContentFilter:用于处理 PUT 请求中的表单数据。
OpenEntityManagerInViewFilter:用于在 Web 请求结束时自动关闭 EntityManager,以避免因为长时间持有 EntityManager 导致的内存泄漏。
DelegatingFilterProxy:是一个通用的过滤器代理,可以将过滤器的实现委托给 Spring 容器中的任意一个 Bean 实例。
需要注意的是,除了 DispatcherServlet 外,其他的 Servlet 大多是一些辅助性的组件,用于解决一些特定的问题,如处理 PUT 请求中的表单数据、自动关闭 EntityManager 等。开发者在使用 Spring 框架时,一般只需要关注 DispatcherServlet 的配置和使用即可。
6、DispatcherServlet 的生命周期
DispatcherServlet 是 Spring MVC 框架中的一个核心组件,它作为前置控制器负责接收客户端请求并将请求分发给相应的 Controller 进行处理。它的生命周期可以分为以下几个阶段:
-
初始化阶段(Initialization):在容器启动时,Servlet 容器会自动创建 DispatcherServlet 实例并调用其 init() 方法进行初始化。在初始化阶段中,DispatcherServlet 会读取并解析配置文件(如 web.xml 或注解配置等),创建必要的组件对象(如 HandlerMapping、HandlerAdapter 等),并将它们存储在 ServletContext 中,以便于在后续的请求处理中使用。
-
请求处理阶段(Request Handling):当客户端发送请求时,Servlet 容器会将请求交给 DispatcherServlet 进行处理。在请求处理阶段中,DispatcherServlet 会根据配置的 HandlerMapping 找到匹配的 Controller,然后调用相应的 Controller 方法进行处理。在方法执行过程中,DispatcherServlet 会根据配置的 HandlerAdapter 对请求参数进行解析,将其转换为方法所需的类型,并将方法的执行结果封装为 ModelAndView 对象。最后,DispatcherServlet 会调用 ViewResolver 将 ModelAndView 对象转换为相应的视图,并将视图渲染后的结果返回给客户端。
-
销毁阶段(Destruction):当 Servlet 容器关闭时,会调用 DispatcherServlet 的 destroy() 方法进行销毁。在销毁阶段中,DispatcherServlet 会释放所有的资源(如 HandlerMapping、HandlerAdapter 等),并清除 ServletContext 中的相应对象。
需要注意的是,由于 DispatcherServlet 是一个单例对象,因此它的生命周期与整个应用程序的生命周期是一致的,即在应用程序启动时创建,在应用程序关闭时销毁。在处理请求时,DispatcherServlet 会创建一个新的线程来处理每个请求,因此它的请求处理阶段是并发的,可以处理多个请求同时到达的情况。
DispatcherServlet 异步处理请求的源码展示:
在 DispatcherServlet 中创建新的线程来处理请求的代码位于 doService() 方法中。以下是部分源码示例:
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 获取处理器
HandlerExecutionChain mappedHandler = getHandler(request);
// 获取处理器适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 使用 Callable 封装请求和响应
final ModelAndView exMv = ha.handle(request, response, mappedHandler.getHandler());
// 创建新线程处理请求
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 判断是否支持异步处理
if (exMv != null && exMv.isAsyncStarted()) {
if (isAsyncDispatch(request)) {
// 使用 Spring 的异步机制进行处理
if (request.getAsyncContext().getTimeout() > 0) {
asyncManager.setTaskTimeout(request.getAsyncContext().getTimeout());
}
asyncManager.startCallableProcessing(new DispatcherServletCallable(exMv), request, response, mappedHandler.getHandler());
return;
}
}
// 同步处理请求
ha.handle(request, response, mappedHandler.getHandler());
}
可以看到,在 doService() 方法中,当判断可以使用异步处理时,DispatcherServlet 会使用 Spring 的异步机制进行处理,其中 asyncManager.startCallableProcessing() 方法会创建一个新的线程来处理请求。这样就实现了异步处理请求,提高了系统的吞吐量和响应速度。
7、Servlet 生命周期和 ServletRequest 生命周期的区别
Servlet的生命周期是整个应用程序中Servlet的初始化、请求处理和销毁的过程,而ServletRequest的生命周期是单次请求中HttpServletRequest和HttpServletResponse对象的创建和销毁的过程。
具体来说,当Servlet容器接收到一个请求时,容器会为该请求创建一个新的HttpServletRequest对象和HttpServletResponse对象,这两个对象只在当前请求中有效,处理完请求后会被销毁,如果是同一个客户端发起的新请求,Servlet容器会再次创建HttpServletRequest和HttpServletResponse对象。// req和rep只在当前请求中有效,处理完请求后会被销毁,可以理解为方法的出栈
在ServletRequest的生命周期中,HttpServletRequest和HttpServletResponse对象会被传递给Servlet的service()方法,Servlet可以通过这两个对象获取请求参数、设置响应头、发送响应数据等操作。当请求处理完毕后,Servlet容器会自动销毁HttpServletRequest和HttpServletResponse对象,释放资源。
所以,Servlet的生命周期是应用程序级别的,而ServletRequest的生命周期是请求级别的。