02创建DispatcherServlet来处理所有的请求

简介: 1.Servlet的生命周期2.DispatcherServlet的类结构体系3.让DispatcherServlet来处理所有的请求

涉及内容预览


  • Servlet的生命周期
  • DispatcherServlet的类结构体系
  • 让DispatcherServlet来处理所有的请求


Servlet的生命周期


在example-goal项目的web.xml配置中就配置了一个DispatcherServlet,DispatcherServlet说白了就是spring实现的一个Servlet,在一定程度上和我们自定义的UserServlet没有什么区别。


既然也是一个Servlet,那就有必要了解下Servlet的生命周期。


Servlet接口定义了如下五个方法


  • init(ServletConfig config)
  • service(ServletRequest req, ServletResponse res)
  • destroy()
  • getServletConfig()
  • getServletInfo()


init方法


在Servlet实例化之后,Servlet容器会调用init()方法来初始化该对象,主要是为了让Servlet对象在处理用户请求前可以完成一些初始化的工作,例如:建立数据库的连接,获取配置信息等。


对于每一个Servlet实例,init()方法只能被调用一次。init()方法有一个类型为ServletConfig的参数,Servlet容器通过这个参数向Servlet传递配置信息。Servlet使用ServletConfig对象从web应用程序的配置信息中获取以key-value形式提供的初始化参数。


另外在Servlet中,还可以通过ServletConfig对象获取描述Servlet运行环境的ServletContext对象,使用该对象,Servlet可以和Servlet容器进行通信。


service方法


容器调用service方法来处理客户端的请求。需要注意的是在service方法被容器调用之前,必需确保init方法正确完成。


容器会构造一个表示客户端请求信息的请求对象(类型为ServletRequest)和一个用于对客户端进行响应的响应对象(类型为ServletResponse)作为参数传递给service方法。在service方法中,Servlet对象通过ServletRequest对象得到客户端的相关信息和请求信息;在对请求进行处理后,调用ServletResponse对象的方法设置响应信息。


destroy方法


当容器检测到一个Servlet对象应该从服务中被移除的时候,容器会调用该对象的destroy方法。以便让Servlet对象可以释放它所使用的资源、保存数据到持久存储设备中,例如:将内存中的数据保存到数据库中、关闭数据库的连接等。


当需要释放内存或者容器关闭时,容器就会调用Servlet对象的destroy方法。在Servlet容器调用destroy方法前,如果还有其他的线程正在service方法中执行,容器会等待这些线程执行完毕或者等待服务器设置的超时时间到达。


一旦Servlet对象的destroy方法被调用,容器不会再把其他的请求发送给该对象。如果需求该Servlet再次为客户端服务,容器将会重新产生一个Servlet对象来处理客户端的请求。在destroy方法调用之后,容器会释放这个Servlet对象,在随后的时间内,该对象会被Java的垃圾收集器回收。


getServletConfig方法


该方法返回容器调用init方法时传递给Servlet对象的ServletConfig对象,ServletConfig对象包含了Servlet的初始化参数。


getServletInfo方法


返回一个String类型的字符串。包括了Servlet的信息,例如:作者、版本、版权。


DispatcherServlet的类结构体系


网络异常,图片无法展示
|


正如Servlet生命周期所描述,当http请求到达HttpServlet的service方法后:


  1. HttpServlet的service方法会将ServletRequestServletResponse转成HttpServletRequestHttpServletResponse,同时调用内部的方法交给service(HttpServletRequest request, HttpServletResponse response)处理
  2. FrameworkServlet类重写了HttpServlet的service(HttpServletRequest request, HttpServletResponse response)方法所以http会到这里。起始FrameworkServlet不仅仅重写了这一个方法,还有如下方法doGet、doPost、doPut等。另外在service方法里做了对PATCH请求方式的支持。但最终都会交给processRequest(HttpServletRequest request, HttpServletResponse response)方法
  3. processRequest(HttpServletRequest request, HttpServletResponse response)方法中,比较重要的就是调用模版方法doService(HttpServletRequest request, HttpServletResponse response),该方法是个抽象方法由DispatcherServlet实现


实现DispatcherServlet处理接收所有的请求


了解了一个http请求从HttpServlet到DispatchServlet的流程,那代码实现也就简单了。那这个主干流程是如何发现的呢?答案就是debug。在UserController的业务方法内进行断点,调用暴露的接口触发断点,这个时候查看当前现场的调用栈情况这个流程也就梳理出来了。


网络异常,图片无法展示
|


spring实现该流程过程中每个方法都做了较多的处理,为了简化我们只对主干方法进行实现。主要代码结构如下


网络异常,图片无法展示
|


public abstract class FrameworkServlet extends HttpServletBean {


   protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws Exception;


   @Override

   protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

       HttpMethod httpMethod = HttpMethod.resolve(req.getMethod());

       if (null == httpMethod || HttpMethod.PATCH == httpMethod) {

           processRequest(req, resp);

       } else {

           super.service(req, resp);

       }

   }



   @Override

   protected final void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

       processRequest(req, resp);

   }


   @Override

   protected final void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

       processRequest(req, resp);

   }


   @Override

   protected final void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

       processRequest(req, resp);

   }


   @Override

   protected final void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

       processRequest(req, resp);

   }


   protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

       try {

           doService(request, response);

       } catch (ServletException | IOException e) {

           throw e;

       } catch (Throwable e) {

           throw new NestedServletException("Request processing failed", e);

       }

   }

}


FrameworkServlet重写了HttpServlet的service、doGet、doPost、doPut、doDelete等方法,并都交给processRequest方法进行处理,在processRequest方法中调用自己定义的抽象模版方法doService。


DispatcherServlet继承FrameworkServlet并实现了模仿方法doService


public class DispatcherServlet extends FrameworkServlet {


   @Override

   protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {

       doDispatch(request, response);

   }


   protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {

       logger.info("接收到请求:path: " + request.getRequestURI());

   }

}


测试


在example-easy-spring项目中使用上面实现的DispatcherServlet来接收所有的请求。


example-easy-spring结构如下


网络异常,图片无法展示
|


主要就是web.xml中声名一个Servlet即我们自己实现的DispatchServlet,同时生命该DispatcherServlet匹配所有的url。


<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"

        version="4.0">



   <!--    设置Springmvc前端处理器-->

   <servlet>

       <servlet-name>DispatcherServlet</servlet-name>

       <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

   </servlet>


   <servlet-mapping>

       <servlet-name>DispatcherServlet</servlet-name>

       <url-pattern>/*</url-pattern>

   </servlet-mapping>


</web-app>


启动该项目调用该服务暴露的端口,可以看到输出的日志。


网络异常,图片无法展示
|


11-Jun-2022 10:52:52.982 INFO [http-nio-8080-exec-1] org.springframework.web.servlet.DispatcherServlet.doDispatch 接收到请求:path: /

11-Jun-2022 10:52:52.986 INFO [http-nio-8080-exec-1] org.springframework.web.servlet.DispatcherServlet.doDispatch 接收到请求:path: /

11-Jun-2022 10:52:53.063 INFO [http-nio-8080-exec-2] org.springframework.web.servlet.DispatcherServlet.doDispatch 接收到请求:path: /

11-Jun-2022 10:53:13.607 INFO [http-nio-8080-exec-5] org.springframework.web.servlet.DispatcherServlet.doDispatch 接收到请求:path: /hello

相关文章
|
前端开发 Java 应用服务中间件
配置SpringMVC的前端控制器DispatcherServlet时,<url-pattern>中“/“和“/*“的区别
配置SpringMVC的前端控制器DispatcherServlet时,<url-pattern>中“/“和“/*“的区别
|
Java Spring
05DispatcherServlet的初始化策略
流程回顾及后续规划 接收到spring之后回调之后的初始化策略
132 0
|
前端开发
DispatcherServlet源码注解分析
DispatcherServlet源码注解分析
149 0
SpringMVC源码解析DispatcherServlet#doDispatch方法流程(下)
SpringMVC源码解析DispatcherServlet#doDispatch方法流程(下)
307 0
SpringMVC源码解析DispatcherServlet#doDispatch方法流程(下)
|
设计模式 开发框架 前端开发
SpringMVC源码解析DispatcherServlet#doDispatch方法流程(上)
SpringMVC源码解析DispatcherServlet#doDispatch方法流程(上)
257 0
SpringMVC源码解析DispatcherServlet#doDispatch方法流程(上)
|
Java 容器 Spring
DispatcherServlet
路径 org.springframework.web.servlet.DispatcherServlet 继承关系 重点关注doService()方法 该方法重写了父类FrameworkServlet的方法 FrameworkServlet在processRequest()方法中调用了...
832 0
|
前端开发 Java Spring
DispatcherServlet源码分析
DispatcherServlet处理request DispatcherServlet是前端控制器, Spring MVC遵循前端控制器模式,前端控制器是MVC 模式中C的一部分, 除此之外,C还包括我们定义的Controller等应用控制器类。
754 0
DispatcherServlet请求流程解析-doDispatch(三)
上篇文章我们主要看了DispatcherServlet在提供服务之间做的初始化工作,大部门工作都在WebApplicationContext中完成,然后WebApplicationContext是DispatcherServlet的一个属性。
|
Java Spring 容器
DispatcherServlet请求流程解析-initWebApplicationContext(二)
上面一篇文章提到,在Servlet初始化的时候,获取属性后调用initServletBean,这个方法会initWebApplicationContext,这是DispatcherServlet对后面的处理做了很多的预先准备工作,我们一起来看看它到底做了什么事情。
|
JavaScript Java Spring
DispatcherServlet-静态资源服务原理(四)
在请求静态资源文件的时候,Spring内部是如何实现的呢?之前对这个问题,有疑问,但如果是自己处理静态的资源都是统一的使用IOUtils.copy(input,output)中去,想了解下Spring内部是如何做的。