【spring】DispatcherServlet详解 您所在的位置:网站首页 Servlet配置 【spring】DispatcherServlet详解

【spring】DispatcherServlet详解

2024-06-26 16:16| 来源: 网络整理| 查看: 265

文章目录 1. 概述1.1 DispatcherServlet类相关的结构图 2. DispatcherServlet的初始化程序2.1 HttpServletBean 的 init() 方法2.1.1 FrameworkServlet 的 initServletBean() 方法2.1.1.1 DispatcherServlet 的 onRefresh() 方法 2.2 initStrategies()2.2.1 initHandlerMappings 方法2.2.2 initHandlerAdapters 方法 3. 响应请求3.1 DispatcherServlet.doService() 4. 总结参考 Spring MVC的web.xml配置详解 完整详细的配置 ContextLoaderListener和DispatcherServlet区别(contextConfigLocation、contextClass参数)&父子容器 web.xml 解释ContextLoaderListener和DispatcherServlet区别 DispatcherServlet详解DispatcherServlet是请求入口,覆盖了springmvc的整个流程 handlerMapping接口和实现类 解释如何内置了几个实现类

1. 概述

在整个 Spring MVC 框架中,DispatcherServlet 处于核心位置,它负责协调和组织不同组件完成请求处理并返回响应工作。DispatcherServlet 是 SpringMVC统一的入口,所有的请求都通过它。

DispatcherServlet 是前端控制器,配置在web.xml文件中,Servlet依自已定义的具体规则拦截匹配的请求,分发到目标Controller来处理。 初始化 DispatcherServlet时,该框架在web应用程序WEB-INF目录中寻找一个名为[servlet-名称]-servlet.xml的文件,并在那里定义相关的Beans,重写在全局中定义的任何Beans。

可以通过contextConfigLocation参数修改加载的][servlet-名称]-servlet.xml的路径

配置示例:

dispatcher org.springframework.web.servlet.DispatcherServlet contextConfigLocation /WEB-INF/spring/spring-servlet.xml 1

在看DispatcherServlet 类之前,我们先来看一下请求处理的大致流程: 在这里插入图片描述

Tomcat 启动,对 DispatcherServlet 进行实例化,然后调用它的初始化方法进行初始化,在这个初始化过程中完成了:对 web.xml 中初始化参数的加载;建立 WebApplicationContext(SpringMVC的IOC容器);进行组件的初始化;

初始化方法并不是单指initStrategies(),因为DispatcherServlet有好多父类,初始化的源头在父类中定义的,initStrategies()只是初始化过程的一部分

客户端发出请求,由 Tomcat 接收到这个请求,如果匹配 DispatcherServlet 在 web.xml中配置的映射路径,Tomcat 就将请求转交给 DispatcherServlet 处理;

DispatcherServlet 从容器中取出所有 HandlerMapping 实例(每个实例对应一个 HandlerMapping接口的实现类)并遍历,每个 HandlerMapping 会根据请求信息,通过自己实现类中的方式去找到处理该请求的 Handler(执行程序,如Controller中的方法),并且将这个 Handler 与一堆 HandlerInterceptor (拦截器)封装成一个 HandlerExecutionChain 对象,一旦有一个 HandlerMapping 可以找到 Handler则退出循环;

DispatcherServlet 取出 HandlerAdapter 组件,根据已经找到的 Handler,再从所有HandlerAdapter 中找到可以处理该 Handler 的 HandlerAdapter 对象;

执行 HandlerExecutionChain 中所有拦截器的 preHandler() 方法,然后再利用 HandlerAdapter 执行 Handler ,执行完成得到 ModelAndView,再依次调用拦截器的 postHandler() 方法;

利用 ViewResolver 将 ModelAndView 或是 Exception(可解析成 ModelAndView)解析成View,然后 View 会调用 render() 方法再根据 ModelAndView 中的数据渲染出页面;

最后再依次调用拦截器的 afterCompletion() 方法,这一次请求就结束了。

1.1 DispatcherServlet类相关的结构图

在这里插入图片描述 由上图得知,DispatcherServlet本质上也是一个Servlet,那么也会有初始化和响应请求的方法。

2. DispatcherServlet的初始化程序

在这里插入图片描述 DispatcherServlet 继承自 HttpServlet,它遵循 Servlet 里的“init-service-destroy”三个阶段,首先我们先来看一下它的 init() 阶段。

2.1 HttpServletBean 的 init() 方法

DispatcherServlet 的 init() 方法在其父类 HttpServletBean中实现的,它覆盖了 GenericServlet 的 init() 方法,主要作用是加载 web.xml 中 DispatcherServlet 的 配置,并调用子类的初始化。

下面是 init() 方法的具体代码:

@Override public final void init() throws ServletException { try { // ServletConfigPropertyValues 是静态内部类,使用 ServletConfig 获取 web.xml 中配置的参数 PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); // 使用 BeanWrapper 来构造 DispatcherServlet BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); initBeanWrapper(bw); bw.setPropertyValues(pvs, true); } catch (BeansException ex) {} // [2]让子类实现的方法,这种在父类定义在子类实现的方式叫做模版方法模式 initServletBean(); }

[2]处的代码,让子类实现的方法initServletBean();

2.1.1 FrameworkServlet 的 initServletBean() 方法

在 HttpServletBean 的 init() 方法中调用了 initServletBean() 这个方法,它是在 FrameworkServlet 类中实现的,主要作用是建立 WebApplicationContext 容器(有时也称上下文),并加载 SpringMVC 配置文件中定义的 Bean 到改容器中,最后将该容器添加到 ServletContext 中。

FrameworkServlet 初始化WebApplicationContext 容器时,会把web IOC容器(),即根容器作为自己的父容器,也叫父上下文

下面是 initServletBean() 方法的具体代码:

@Override protected final void initServletBean() throws ServletException { try { // 初始化 WebApplicationContext (即SpringMVC的IOC容器) this.webApplicationContext = initWebApplicationContext(); initFrameworkServlet(); } catch (ServletException ex) { } catch (RuntimeException ex) { } }

WebApplicationContext 继承于 ApplicationContext 接口,从容器中可以获取当前应用程序环境信息,它也是 SpringMVC 的 IOC 容器。

下面是 initWebApplicationContext() 方法的具体代码:

protected WebApplicationContext initWebApplicationContext() { // 获取 ContextLoaderListener 初始化并注册在 ServletContext 中的根容器,即 Spring 的容器 WebApplicationContext rootContext =     WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { // 因为 WebApplicationContext 不为空,说明该类在构造时已经将其注入 wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { if (cwac.getParent() == null) { // 将 Spring 的容器设为 SpringMVC 容器的父容器 cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) {   // 如果 WebApplicationContext 为空,则进行查找,能找到说明上下文已经在别处初始化。   wac = findWebApplicationContext(); } if (wac == null) { // 如果 WebApplicationContext 仍为空,则以 Spring 的容器为父上下文建立一个新的。 wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { //[2] 模版方法,由 DispatcherServlet 实现 onRefresh(wac); } if (this.publishContext) { // 发布这个 WebApplicationContext 容器到 ServletContext 中 String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); } return wac; }

[2] 模版方法,由 DispatcherServlet 实现onRefresh()

2.1.1.1 DispatcherServlet 的 onRefresh() 方法

建立好 WebApplicationContext(上下文) 后,通过 onRefresh(ApplicationContext context) 方法回调,进入 DispatcherServlet 类中。onRefresh() 方法,提供 SpringMVC 的初始化,具体代码如下:

@Override protected void onRefresh(ApplicationContext context) { initStrategies(context); } 2.2 initStrategies()

initStrategies()方法完成SpringMVC 的初始化。

具体初始化了什么,可以在其initStrategies()方法中知晓,这个方法如下:

protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); //'重要' initHandlerAdapters(context); //'重要' initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }

需要做的八件事情如下所述:

initMultipartResolver:初始化MultipartResolver,用于处理文件上传服务,如果有文件上传,那么就会将当前的HttpServletRequest包装成- DefaultMultipartHttpServletRequest,并且将每个上传的内容封装成CommonsMultipartFile对象。需要在dispatcherServlet-servlet.xml中配置文件上传解析器。initLocaleResolver:用于处理应用的国际化问题,本地化解析策略。initThemeResolver:用于定义一个主题。initHandlerMapping:用于定义请求映射关系。initHandlerAdapters:用于根据Handler的类型定义不同的处理规则。initHandlerExceptionResolvers:当Handler处理出错后,会通过此将错误日志记录在log文件中,默认实现类是SimpleMappingExceptionResolver。initRequestToViewNameTranslators:将指定的ViewName按照定义的RequestToViewNameTranslators替换成想要的格式。initViewResolvers:用于将View解析成页面。initFlashMapManager:用于生成FlashMap管理器。

完整的时序图: 在这里插入图片描述

2.2.1 initHandlerMappings 方法

initHandlerMappings() 方法从 SpringMVC 的容器及 Spring 的容器中查找所有的 HandlerMapping 实例,并把它们放入到 handlerMappings 这个 list 中。

这个方法并不是对 HandlerMapping 实例的创建,这里只是查找并放到一个集合中。HandlerMapping 实例化是在之前步骤 WebApplicationContext 容器初始化中,即 SpringMVC 容器初始化的时候创建的。

如果不在 springmvc.xml 文件中配置,就会使用默认的。参见《SpringMVC 默认配置 DispatcherServlet.properties 文件》

2.2.2 initHandlerAdapters 方法

这个方法的逻辑和initHandlerMappings 方法差不多,这里就不在单独列出了。

3. 响应请求

HttpServlet 提供了 doGet()、doPost() 等方法,DispatcherServlet 中这些方法是在其父类 FrameworkServlet 中实现的,代码如下:

FrameworkServlet .doGet()

@Override protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); }

这些方法又都调用了 processRequest() 方法,我们来看一下代码:

protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null; // 返回与当前线程相关联的 LocaleContext LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); // 根据请求构建 LocaleContext,公开请求的语言环境为当前语言环境 LocaleContext localeContext = buildLocaleContext(request); // 返回当前绑定到线程的 RequestAttributes RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); // 根据请求构建ServletRequestAttributes ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); // 获取当前请求的 WebAsyncManager,如果没有找到则创建 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); // 使 LocaleContext 和 requestAttributes 关联 initContextHolders(request, localeContext, requestAttributes); try { //[2] 由 DispatcherServlet 实现 doService(request, response); } catch (ServletException ex) { } catch (IOException ex) { } catch (Throwable ex) { } finally { // 重置 LocaleContext 和 requestAttributes,解除关联 resetContextHolders(request, previousLocaleContext, previousAttributes); if (requestAttributes != null) { requestAttributes.requestCompleted(); }// 发布 ServletRequestHandlerEvent 事件 publishRequestHandledEvent(request, startTime, failureCause); } }

核心代码是 [2] 由 DispatcherServlet 实现 的 doService(request, response);

3.1 DispatcherServlet.doService()

DispatcherServlet 的 doService() 方法主要是设置一些 request 属性,并调用 doDispatch() 方法进行请求分发处理,doDispatch() 方法的主要过程是通过 HandlerMapping 获取 Handler,再找到用于执行它的 HandlerAdapter,执行 Handler 后得到 ModelAndView ,ModelAndView 是连接“业务逻辑层”与“视图展示层”的桥梁,接下来就要通过 ModelAndView 获得 View,再通过它的 Model 对 View 进行渲染。doDispatch() 方法如下:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; // 获取当前请求的WebAsyncManager,如果没找到则创建并与请求关联 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try { // 检查是否有 Multipart,有则将请求转换为 Multipart 请求 processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); //[1] 遍历所有的 HandlerMapping 找到与请求对应的 Handler,并将其与一堆拦截器封装到 HandlerExecution 对象中。 mappedHandler = getHandler(processedRequest); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } //[2] 遍历所有的 HandlerAdapter,找到可以处理该 Handler 的 HandlerAdapter HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // 处理 last-modified 请求头 String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } // [3] 遍历拦截器,执行它们的 preHandle() 方法 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } try { //[4] 执行实际的处理程序 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); } finally { if (asyncManager.isConcurrentHandlingStarted()) { return; } } applyDefaultViewName(request, mv); // [5] 遍历拦截器,执行它们的 postHandle() 方法 mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } //[6] 处理执行结果,是一个 ModelAndView 或 Exception,然后进行渲染 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { } catch (Error err) { } finally { if (asyncManager.isConcurrentHandlingStarted()) { //[7] 遍历拦截器,执行它们的 afterCompletion() 方法 mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); return; } // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } }

核心逻辑是[1]-[6]

[1] 遍历所有的 HandlerMapping 找到与请求对应的 Handler,并将其与一堆拦截器封装到 HandlerExecution 对象中。[2] 遍历所有的 HandlerAdapter,找到可以处理该 Handler 的 HandlerAdapter[3] 遍历拦截器,执行它们的 preHandle() 方法[4] 执行实际的处理程序[5] 遍历拦截器,执行它们的 postHandle() 方法[6] 处理执行结果,是一个 ModelAndView 或 Exception,然后进行渲染 4. 总结 DispatcherServlet是spring mvc核心类DispatcherServlet负责初始化 HandlerMappings (参见 ”2.2.1 initHandlerMappings 方法“章节)和HandlerAdapter(参见 “initHandlerAdapters 方法”章节)响应请求,会利用之前初始化的HandlerMappings 、HandlerAdapter,最终找到ModelAndView接下来就要通过 ModelAndView 获得 View,再通过它的 Model 对 View 进行渲染 参考

《DispatcherServlet详解》



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有