spring mvc DispatcherServlet之前端控制器架构详解

前端控制器是整个MVC框架中最为核心的一块,它主要用来拦截符合要求的外部请求,并把请求分发到不同的控制器去处理,根据控制器处理后的结果,生成相应的响应发送到客户端。前端控制器既可以使用Filter实现(Struts2采用这种方式),也可以使用Servlet来实现(spring MVC框架)。

DispatcherServlet 作为前置控制器是web服务器的入口,是spring mvc最重要的一个类,通过它的生命周期可以加深对web服务器的理解。

servlet的生命周期

首先我们回忆一下servlet的生命周期:

Servlet生命周期分为三个阶段:【Servlet生命周期与工作原理详解】

  1.初始化阶段  调用init()方法。Servlet被装载后,Servlet容器创建一个Servlet实例并且调用Servlet的init()方法进行初始化。在Servlet的整个生命周期内,init()方法只被调用一次。

  2.响应客户请求阶段  调用service()方法

  3.终止阶段  调用destroy()方法

Servlet初始化阶段

  在下列时刻Servlet容器装载Servlet:

  1.Servlet容器启动时自动装载某些Servlet,实现它只需要在web.XML文件中的<Servlet></Servlet>之间添加如下代码:  

<loadon-startup>1</loadon-startup>
  2.在Servlet容器启动后,客户首次向Servlet发送请求

  3.Servlet类文件被更新后,重新装载Servlet

DispatcherServlet的结构

复习了上述知识后我们来看看DispatcherServlet的结构:

DispatcherServlet继承自抽象类:FrameworkServlet,间接继承了HttpServlet (FrameworkServlet继承自HttpServletBean,而HttpServletBean继承自HttpServlet )

Servlet的初始化

 protected void initStrategies(ApplicationContext context) {
 initMultipartResolver(context); //文件上传解析,如果请求类型是multipart将通过MultipartResolver进行文件上传解析;
 initLocaleResolver(context); //本地化解析
 initThemeResolver(context);   //主题解析
 initHandlerMappings(context); //通过HandlerMapping,将请求映射到处理器
 initHandlerAdapters(context); //通过HandlerAdapter支持多种类型的处理器
 initHandlerExceptionResolvers(context); //如果执行过程中遇到异常将交给HandlerExceptionResolver来解析
 initRequestToViewNameTranslator(context); //直接解析请求到视图名
 initViewResolvers(context); //通过ViewResolver解析逻辑视图名到具体视图实现
 initFlashMapManager(context); //flash映射管理器
 }

servlet如何处理请求:

servlet的service方法处理http请求。

FrameworkServlet.java 定义了servlet的service和destroy方法,如下所示:

 /**
 * Override the parent class implementation in order to intercept PATCH
 * requests.
 */
 @Override
 protected void service(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException {

 String method = request.getMethod();
 if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) {
  processRequest(request, response);
 }
 else {
  super.service(request, response);
 }
 }

我们知道http请求类型有七种(外加一个option选项),定义如下:

public enum RequestMethod {
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE

}

FrameworkServlet的service()处理不同的请求,我们以常见的post来说明:

 /**
 * Process this request, publishing an event regardless of the outcome.
 * <p>The actual event handling is performed by the abstract
 * {@link #doService} template method.
 */
 protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException {

 long startTime = System.currentTimeMillis();
 Throwable failureCause = null;

 LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
 LocaleContext localeContext = buildLocaleContext(request);

 RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
 ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
 asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

 initContextHolders(request, localeContext, requestAttributes);

 try {
  doService(request, response);
 }
 catch (ServletException ex) {
  failureCause = ex;
  throw ex;
 }
 catch (IOException ex) {
  failureCause = ex;
  throw ex;
 }
 catch (Throwable ex) {
  failureCause = ex;
  throw new NestedServletException("Request processing failed", ex);
 }

 finally {
  resetContextHolders(request, previousLocaleContext, previousAttributes);
  if (requestAttributes != null) {
  requestAttributes.requestCompleted();
  }

  if (logger.isDebugEnabled()) {
  if (failureCause != null) {
   this.logger.debug("Could not complete request", failureCause);
  }
  else {
   if (asyncManager.isConcurrentHandlingStarted()) {
   logger.debug("Leaving response open for concurrent processing");
   }
   else {
   this.logger.debug("Successfully completed request");
   }
  }
  }

  publishRequestHandledEvent(request, startTime, failureCause);
 }
 }

FrameworkServlet 抽象定义了处理流程,留待子类来实现该方法,完成具体的请求处理。

/**
 * Subclasses must implement this method to do the work of request handling,
 * receiving a centralized callback for GET, POST, PUT and DELETE.
 * <p>The contract is essentially the same as that for the commonly overridden
 * {@code doGet} or {@code doPost} methods of HttpServlet.
 * <p>This class intercepts calls to ensure that exception handling and
 * event publication takes place.
 * @param request current HTTP request
 * @param response current HTTP response
 * @throws Exception in case of any kind of processing failure
 * @see javax.servlet.http.HttpServlet#doGet
 * @see javax.servlet.http.HttpServlet#doPost
 */
 protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
  throws Exception;

具体实现如下:

 /**
 * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
 * for the actual dispatching.
 */
 @Override
 protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
 if (logger.isDebugEnabled()) {
  String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
  logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
   " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
 }

 // Keep a snapshot of the request attributes in case of an include,
 // to be able to restore the original attributes after the include.
 Map<String, Object> attributesSnapshot = null;
 if (WebUtils.isIncludeRequest(request)) {
  attributesSnapshot = new HashMap<String, Object>();
  Enumeration<?> attrNames = request.getAttributeNames();
  while (attrNames.hasMoreElements()) {
  String attrName = (String) attrNames.nextElement();
  if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
   attributesSnapshot.put(attrName, request.getAttribute(attrName));
  }
  }
 }

 // Make framework objects available to handlers and view objects.
 request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
 request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
 request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
 request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

 FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
 if (inputFlashMap != null) {
  request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
 }
 request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
 request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

 try {
  doDispatch(request, response);
 }
 finally {
  if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
  return;
  }
  // Restore the original attribute snapshot, in case of an include.
  if (attributesSnapshot != null) {
  restoreAttributesAfterInclude(request, attributesSnapshot);
  }
 }
 }

重头戏,作为请求分发器的实现:

功能:1. 把请求分发到handler(按照配置顺序获取servlet的映射关系获取handler);2. 根据servlet已安装的  HandlerAdapters 去查询第一个能处理的handler;3. handler激发处理请求

 /**
 * Process the actual dispatching to the handler.
 * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
 * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
 * to find the first that supports the handler class.
 * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
 * themselves to decide which methods are acceptable.
 * @param request current HTTP request
 * @param response current HTTP response
 * @throws Exception in case of any kind of processing failure
 */
 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
 HttpServletRequest processedRequest = request;
 HandlerExecutionChain mappedHandler = null;
 boolean multipartRequestParsed = false;

 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

 try {
  ModelAndView mv = null;
  Exception dispatchException = null;

  try {
  processedRequest = checkMultipart(request);
  multipartRequestParsed = (processedRequest != request);

  // Determine handler for the current request.
  mappedHandler = getHandler(processedRequest);
  if (mappedHandler == null || mappedHandler.getHandler() == null) {
   noHandlerFound(processedRequest, response);
   return;
  }

  // Determine handler adapter for the current request.
  HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

  // Process last-modified header, if supported by the handler.
  String method = request.getMethod();
  boolean isGet = "GET".equals(method);
  if (isGet || "HEAD".equals(method)) {
   long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
   if (logger.isDebugEnabled()) {
   logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
   }
   if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
   return;
   }
  }

  if (!mappedHandler.applyPreHandle(processedRequest, response)) {
   return;
  }

  try {
   // Actually invoke the handler.
   mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  }
  finally {
   if (asyncManager.isConcurrentHandlingStarted()) {
   return;
   }
  }

  applyDefaultViewName(request, mv);
  mappedHandler.applyPostHandle(processedRequest, response, mv);
  }
  catch (Exception ex) {
  dispatchException = ex;
  }
  processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
 }
 catch (Exception ex) {
  triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
 }
 catch (Error err) {
  triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
 }
 finally {
  if (asyncManager.isConcurrentHandlingStarted()) {
  // Instead of postHandle and afterCompletion
  mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
  return;
  }
  // Clean up any resources used by a multipart request.
  if (multipartRequestParsed) {
  cleanupMultipart(processedRequest);
  }
 }
 }

servlet销毁

 /**
 * Close the WebApplicationContext of this servlet.
 * @see org.springframework.context.ConfigurableApplicationContext#close()
 */
 @Override
 public void destroy() {
 getServletContext().log("Destroying Spring FrameworkServlet '" + getServletName() + "'");
 // Only call close() on WebApplicationContext if locally managed...
 if (this.webApplicationContext instanceof ConfigurableApplicationContext && !this.webApplicationContextInjected) {
  ((ConfigurableApplicationContext) this.webApplicationContext).close();
 }
 }

小结:

本文因篇章限制,仅仅介绍了请求处理的流程,没有对代码进行深入的分析,接下来的文章将从细微处着手,分析spring的代码之美。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

您可能感兴趣的文章:

  • 浅谈springmvc的DispatcherServlet分析
  • Spring MVC之DispatcherServlet详解_动力节点Java学院整理
  • Spring MVC之DispatcherServlet_动力节点Java学院整理
(0)

相关推荐

  • Spring MVC之DispatcherServlet详解_动力节点Java学院整理

    DispatcherServlet作用 DispatcherServlet是前端控制器设计模式的实现,提供Spring Web MVC的集中访问点,而且负责职责的分派,而且与Spring IoC容器无缝集成,从而可以获得Spring的所有好处. 具体请参考第二章的图2-1. DispatcherServlet主要用作职责调度工作,本身主要用于控制流程,主要职责如下: 1.文件上传解析,如果请求类型是multipart将通过MultipartResolver进行文件上传解析: 2.通过Handle

  • 浅谈springmvc的DispatcherServlet分析

    本文介绍了springmvc的DispatcherServlet,分享给大家,具体如下: 一.程序 (一)web.xml文件中的内容 <!-- springMVC核心配置 --> <servlet> <servlet-name>springmvcServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-cl

  • Spring MVC之DispatcherServlet_动力节点Java学院整理

    Spring MVC之DispatcherServlet 使用Spring MVC,配置DispatcherServlet是第一步. DispatcherServlet是一个Servlet,所以可以配置多个DispatcherServlet. DispatcherServlet是前置控制器,配置在web.xml文件中的.拦截匹配的请求,Servlet拦截匹配规则要自已定义,把拦截下来的请求,依据某某规则分发到目标Controller(我们写的Action)来处理. "某某规则":是根据

  • spring mvc DispatcherServlet之前端控制器架构详解

    前端控制器是整个MVC框架中最为核心的一块,它主要用来拦截符合要求的外部请求,并把请求分发到不同的控制器去处理,根据控制器处理后的结果,生成相应的响应发送到客户端.前端控制器既可以使用Filter实现(Struts2采用这种方式),也可以使用Servlet来实现(spring MVC框架). DispatcherServlet 作为前置控制器是web服务器的入口,是spring mvc最重要的一个类,通过它的生命周期可以加深对web服务器的理解. servlet的生命周期 首先我们回忆一下ser

  • 基于Spring + Spring MVC + Mybatis 高性能web构建实例详解

    一直想写这篇文章,前段时间痴迷于JavaScript.NodeJs.AngularJS,做了大量的研究,对前后端交互有了更深层次的认识. 今天抽个时间写这篇文章,我有预感,这将是一篇很详细的文章,详细的配置,详细的注释,看起来应该很容易懂. 用最合适的技术去实现,并不断追求最佳实践.这就是架构之道. 希望这篇文章能给你们带来一些帮助,同时希望你们可以为这个项目贡献你的想法. 源码地址:https://github.com/Eliteams/quick4j 点击打开 源码地址:https://gi

  • Spring mvc 分步式session的实例详解

    Spring mvc 分步式session的实例详解 Session代表服务器与浏览器的一次会话过程,它的信息是保存在服务器端的.在Servlet中,session指的是HttpSession类的对象.服务器在创建session后,会把sessionid以cookie的形式回写给客户端.只要客户端的浏览器不关,每一次访问服务器都会带上这个sessionid.这样就可以在每次请求的时候获取到session的信息. 下面以spring MVC以例来说明如果创建分步式session. 1.login

  • Spring MVC自定义日期类型转换器实例详解

    Spring MVC自定义日期类型转换器实例详解 WEB层采用Spring MVC框架,将查询到的数据传递给APP端或客户端,这没啥,但是坑的是实体类中有日期类型的属性,但是你必须提前格式化好之后返回给它们.说真的,以前真没这样做过,之前都是一口气查询到数据,然后在jsp页面上格式化,最后展示给用户.但是这次不同,这次我纯属操作数据,没有页面.直接从数据库拿数据给它们返数据.它们给我传数据我持久化数据,说到这里一个小问题就默默的来了. 首先把问题还原一下吧(这是一个数据导出功能),下图中用红框圈

  • Spring MVC 框架搭建配置方法及详解

    现在主流的Web MVC框架除了Struts这个主力 外,其次就是Spring MVC了,因此这也是作为一名程序员需要掌握的主流框架,框架选择多了,应对多变的需求和业务时,可实行的方案自然就多了.不过要想灵活运用Spring MVC来应对大多数的Web开发,就必须要掌握它的配置及原理. 一.Spring MVC环境搭建:(Spring 2.5.6 + Hibernate 3.2.0) 1. jar包引入 Spring 2.5.6:spring.jar.spring-webmvc.jar.comm

  • Spring MVC 启动过程源码分析详解

    今天小编尝试从源码层面上对Spring mvc的初始化过程进行分析,一起揭开Spring mvc的真实面纱,也许我们都已经学会使用spring mvc,或者说对spring mvc的原理在理论上已经能倒背如流.在开始之前,这可能需要你掌握Java EE的一些基本知识,比如说我们要先学会Java EE 的Servlet技术规范,因为Spring mvc框架实现,底层是遵循Servlet规范的. 在开始源码分析之前,我们可能需要一个简单的案例工程,不慌,小编已经安排好了: 样例工程下载地址 : ht

  • Spring MVC文件配置以及参数传递示例详解

    web.xml文件配置 创建好一个SpringMVC项目后,需要在需要在WB-INF文件夹下配置web.xml文件 <?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

  • Spring MVC 自定义数据转换器的思路案例详解

    数据转换器是指将客户端 http 请求中的参数转换为业务方法中定义的形参,自定义表示开发者可以自主设计转换模式,HandlerAdapter 已经提供了通用的转换,比如将 String 转成 int,String 转成 double,表单数据的封装等,但是在特殊的业务场景下,HandlerAdapter 无法进行转换,就需要开发者自定义转换器. 我们需要实现 Converter 接口来协助 Spring MVC 完成数据类型的转换,下面通过两个案例来介绍如何自定义数据转换器. 案例一:客户端输入

  • spring mvc中的@PathVariable动态参数详解

    目录 spring mvc @PathVariable动态参数 spring mvc是如何做到根据参数名动态绑定参数的? 反射获取参数名 -parameters参数 -g参数 ASM SpringMVC的处理方式 总结 spring mvc @PathVariable动态参数 spring mvc中的@PathVariable是用来获得请求url中的动态参数的,十分方便 @Controller public class TestController { @RequestMapping(value

  • Spring MVC常用客户端参数接收方式详解

    在MVC结构中,控制器组件主要的功能就是接收请求.处理请求.生成响应,接收客户端传来的请求参数的往往是控制器要做的第一件事. Book实体类Book.java public class Book { private Integer bookId; private String author; //生成Get.Set方法,此处省略 } 一.直接用参数名匹配请求参数 客户端界面(表单): <form action="/queryString" method="post&qu

随机推荐