JavaEE

1、Servlet的生命周期

Servlet 生命周期指的是 Servlet 对象从创建到销毁的整个过程,包括以下阶段:

  1. 加载和实例化阶段(loading and instantiation):当 Servlet 容器启动或者第一次请求某个 Servlet 时,会加载并创建 Servlet 对象的实例。在此阶段,容器会调用 ServletContext 的 getServlet 方法来获取 Servlet 实例,并调用其 init 方法进行初始化。在 init 方法中,Servlet 可以进行一些初始化工作,如加载配置文件、建立数据库连接等。

  2. 就绪阶段(ready):当 Servlet 初始化完成后,容器会将其放入就绪状态,表示它已经准备好处理客户端请求了。

  3. 请求处理阶段(request handling):当客户端发起请求时,Servlet 容器会为每个请求创建一个新的线程,并调用 Servlet 的 service 方法处理请求。在 service 方法中,Servlet 可以读取请求数据、进行业务处理,并生成响应数据发送给客户端。

  4. 销毁阶段(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会维护一个默认组件列表

处理流程

  1. 客户端发出的一个HTTP请求,WEB应用服务器接受到这个请求,如果匹配DispatcherServlet的请求映射路径(web.xml中指定),则Web容器将该请求转交给DispatcherServlet处理

  2. 接受请求后,DispatcherServlet会迭代上下文中所有的HandlerMapping,直到某个HandlerMapping返回HandlerExecutionChain为止,如:BeanNameUrlHandlerMapping会用URL匹配beanName,找到则封装一个HandlerExecutionChain实例返回。

    HandlerExecutionChain实例封装了一个handler处理对象和一些interceptors,HandlerAdapter使用handler处理对象调用最终的控制器,如:BeanNameUrlHandlerMapping返回的HandlerExecutionChain实例中,handler处理对象为bean的name。

  3. DispatcherServlet迭代上下文中所有的HandlerAdapter,将handler对象作为参数传给HandlerServlet通过调用HnadlerAdapter实例的handle方法进行控制器的调用,调用该方法时,会传递HandlerExecutionChain实例里的handler处理对象,该方法返回ModelAndView

  4. HandlerAdapter在调用handler时,使用HandlerMethodArgumentResolver进行参数解析,不同的参数类别由不同的HandlerMethodArgumentResolver实例处理。

    在进行参数解析时,如果参数是引用类型则需要DataBinder的协助,DataBinder可以完成请求数据到引用类型参数的绑定,以及数据的格式化及数据验证,数据类型转换也由DataBinder完成。

    如果参数要获取所有请求体(如:@ResponseBody),则需要对应的消息转换器

  5. 如果调用过程中出现异常,则会迭代HandlerExceptionResolver,由对应的异常处理器返回ModelAndView

  6. 从返回的ModelAndview中获取视图名称,然后从Spring上下文中获取视图解析器,通过视图解析器处理视图,生成View

  7. 当得到真实的视图对象View后,DispatcherServlet就使用这个View对象对ModelAndView中的模型数据进行视图渲染

  8. 最终客户端得到的响应信息可能是一个普通的HTML页面,也可能是一个XML或者JSON串,甚至是一个图片或者哟个PDF等不同的媒体形式。

4、ssm启动

  1. Tomcat启动时会调用实现了ServletContainerInitializer接口的类,

  2. SpringServletContainerInitializer会调用实现了WebApplicationInitializer接口的类,

  3. AbstractAnnotationConfigDispatcherServletInitializer实现了WebApplicationInitializer。

  4. 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 进行处理。它的生命周期可以分为以下几个阶段:

  1. 初始化阶段(Initialization):在容器启动时,Servlet 容器会自动创建 DispatcherServlet 实例并调用其 init() 方法进行初始化。在初始化阶段中,DispatcherServlet 会读取并解析配置文件(如 web.xml 或注解配置等),创建必要的组件对象(如 HandlerMapping、HandlerAdapter 等),并将它们存储在 ServletContext 中,以便于在后续的请求处理中使用。

  2. 请求处理阶段(Request Handling):当客户端发送请求时,Servlet 容器会将请求交给 DispatcherServlet 进行处理。在请求处理阶段中,DispatcherServlet 会根据配置的 HandlerMapping 找到匹配的 Controller,然后调用相应的 Controller 方法进行处理。在方法执行过程中,DispatcherServlet 会根据配置的 HandlerAdapter 对请求参数进行解析,将其转换为方法所需的类型,并将方法的执行结果封装为 ModelAndView 对象。最后,DispatcherServlet 会调用 ViewResolver 将 ModelAndView 对象转换为相应的视图,并将视图渲染后的结果返回给客户端。

  3. 销毁阶段(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的生命周期是请求级别的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值