目录

  • 1. DispatcherServlet的作用分析
    • 1.1 uml类图
  • 2. DispatcherServlet的初始化分析
    • 2.1 初始化流程
    • 2.2 详细分析流程
  • 3. DispatcherServlet的请求过程分析
    • 3.1 从Servlet调用到DispatcherServlet流程
    • 3.2 DispatcherServlet.doDispatch详细流程

基于spring版本3.2.16.RELEASE

1. DispatcherServlet的作用分析

1.1 uml类图

从类图, 可以看出DispatcherServlet其本质是Servlet, 其间接实现了ServletConfig:

public interface Servlet {void init(ServletConfig var1) throws ServletException;ServletConfig getServletConfig();void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;String getServletInfo();void destroy();
}public interface ServletConfig {String getServletName();ServletContext getServletContext();String getInitParameter(String var1);Enumeration<String> getInitParameterNames();
}

2. DispatcherServlet的初始化分析

2.1 初始化流程

解析是从web.xml开始, DispatcherServlet其本质是Servlet, 所以初始化是从Servlet.init()方法开始:

2.2 详细分析流程

下面从上面几个重要步骤详细分析, 初始化的工作:

  • GenericServlet: init(ServletConfig config)
public void init(ServletConfig config) throws ServletException {this.config = config;this.init();//调用被子类HttpServletBean重写方法init()
}
  • HttpServletBean: init()

为DispatcherServlet创建BeanWrapper, 从ServletConfig中获取Servlet的initParams, 并设置到BeanWrapper的PropertyValues中去, 如contextConfigLocation字段等属性. 核心代码如下:

PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
this.initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);            
<servlet><servlet-name>webServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:web-appcontext.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet>
  • FrameworkServlet: initWebApplicationContext()

创建并初始化WebApplicationContext (以下简称Mvc容器)

protected WebApplicationContext initWebApplicationContext() {WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());//父容器if (wac == null) {wac = this.createWebApplicationContext(rootContext);}if (!this.refreshEventReceived) {this.onRefresh(wac);}if (this.publishContext) {//FrameworkServlet.class.getName() + ".CONTEXT."+getServletName()String attrName = this.getServletContextAttributeName();this.getServletContext().setAttribute(attrName, wac);}return wac;
}protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {Class<?> contextClass = this.getContextClass();//未配置则默认为XmlWebApplicationContextConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);//初始化wac.setEnvironment(this.getEnvironment());//设置Environmentwac.setParent(parent);//设置父容器wac.setConfigLocation(this.getContextConfigLocation()); //设置Spring配置文件位置this.configureAndRefreshWebApplicationContext(wac);return wac;
}protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac){wac.setServletContext(this.getServletContext());//关联ServletContextwac.setServletConfig(this.getServletConfig());wac.setNamespace(this.getNamespace());//设置nameSpace为DispatcherServlet的nameSpacewac.addApplicationListener(new SourceFilteringListener(wac, new FrameworkServlet.ContextRefreshListener()));ConfigurableEnvironment env = wac.getEnvironment();//设置Environmentif (env instanceof ConfigurableWebEnvironment) {((ConfigurableWebEnvironment)env).initPropertySources(this.getServletContext(), this.getServletConfig());}this.postProcessWebApplicationContext(wac);this.applyInitializers(wac);wac.refresh();//初始化所有的bean
}

这里需要注意以下几个点:

1.如果在web.xml中配置了ContextLoaderListener, 则生成的Root ApplicationContext(以下简称Root容器)会从ServletContext属性里拿出来作为当前Mvc容器的parent父容器, 见代码:

public interface WebApplicationContext extends ApplicationContext {String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";//Root WebApplicationContext放在ServletContext属性里
public static WebApplicationContext getWebApplicationContext(ServletContext sc) {return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);}
}

Mvc容器也放在ServletContext属性里, 属性名称为FrameworkServlet.class.getName() + ".CONTEXT."+getServletName()

2.设置Mvc容器的contextConfigLocation

需要注意一个点, 如果web.xml没有配置文件, 最终Mvc容器配置文件默认取${servlet-name}-servlet.xml, 请参考: SpringMVC的默认配置文件位置

  • DispatcherServlet: initStrategies()

主要的动作是初始化DispatcherServlet的9大对象, 从Mvc容器获取对应类型的对象, 部分找不到会有默认值

protected void initStrategies(ApplicationContext context) {this.initMultipartResolver(context);//无默认值this.initLocaleResolver(context);//有默认值this.initThemeResolver(context);//有默认值this.initHandlerMappings(context);//有默认值this.initHandlerAdapters(context);//有默认值this.initHandlerExceptionResolvers(context);//有默认值this.initRequestToViewNameTranslator(context);//有默认值this.initViewResolvers(context);//有默认值this.initFlashMapManager(context);//有默认值
}

各组件的默认值在DispatcherServlet.properties里定义.

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolverorg.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolverorg.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMappingorg.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapterorg.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolverorg.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslatororg.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolverorg.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
  • WebApplicationContext.refresh()

WebApplicationContext.refresh和普通的ApplicationContext.refresh相比, 有所增强

AbstractApplicationContext.java

public void refresh() throws BeansException, IllegalStateException {synchronized(this.startupShutdownMonitor) {this.prepareRefresh();ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();this.prepareBeanFactory(beanFactory);try {this.postProcessBeanFactory(beanFactory);//增强的方法this.invokeBeanFactoryPostProcessors(beanFactory);this.registerBeanPostProcessors(beanFactory);this.initMessageSource();this.initApplicationEventMulticaster();this.onRefresh();this.registerListeners();this.finishBeanFactoryInitialization(beanFactory);this.finishRefresh();} catch (BeansException var5) {//略}}}

AbstractRefreshableWebApplicationContext.java

protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));beanFactory.ignoreDependencyInterface(ServletContextAware.class);beanFactory.ignoreDependencyInterface(ServletConfigAware.class);//注册request/session/globalSession等scope相关组件到Mvc容器WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);//将servlet相关组件注册到Mvc容器WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
}

ServletContextAwareProcessor.java

public class ServletContextAwareProcessor implements BeanPostProcessor {private ServletContext servletContext;private ServletConfig servletConfig;public ServletContextAwareProcessor(ServletContext servletContext, ServletConfig servletConfig) {this.servletContext = servletContext;this.servletConfig = servletConfig;if (servletContext == null && servletConfig != null) {this.servletContext = servletConfig.getServletContext();}}public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if (this.servletContext != null && bean instanceof ServletContextAware) {((ServletContextAware)bean).setServletContext(this.servletContext);}if (this.servletConfig != null && bean instanceof ServletConfigAware) {((ServletConfigAware)bean).setServletConfig(this.servletConfig);}return bean;}
}

3. DispatcherServlet的请求过程分析

DispatcherServlet其本质是Servlet, 请求从web容器请求过来调用Servlet的service方法:

3.1 从Servlet调用到DispatcherServlet流程

Servlet.service() -> DispatcherServlet.doDispatch()

下图调用流程比较简单, 略

3.2 DispatcherServlet.doDispatch详细流程

贴出烂大街的处理流程图:

对应源码:

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 {//如果是上传请求,且注册了MultipartResolver//此处会解析文件绑定到request,替换request为MultipartRequestprocessedRequest = this.checkMultipart(request);multipartRequestParsed = processedRequest != request;//如果是@Controller类型的处理器,此处还有校验@RequestMapping注解的三个属性RequestMethod/Headers/ParamsmappedHandler = this.getHandler(processedRequest, false);//获取Handlerif (mappedHandler == null || mappedHandler.getHandler() == null) {this.noHandlerFound(processedRequest, response);return;}//获取HandlerAdapterHandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());String method = request.getMethod();//interceptors过滤器前置处理if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}//调用实际Handler处理请求mv = ha.handle(processedRequest, response, mappedHandler.getHandler());this.applyDefaultViewName(request, mv);mappedHandler.applyPostHandle(processedRequest, response, mv);//过滤器后置} catch (Exception var20) {dispatchException = var20;}//渲染view或处理异常this.processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);} catch (Exception e){//略}
}

相关参考:

  1. DefaultAnnotationHandlerMapping详细分析

  2. HandlerInterceptor过滤器详细分析

DispatcherServlet详细分析相关推荐

  1. Yolov1目标检测算法详细分析

    Yolov1目标检测算法详细分析 Yolov1介绍 这是继RCNN,fast-RCNN 和 faster-RCNN之后,rbg(Ross Girshick)大神挂名的又一大作,起了一个很娱乐化的名字: ...

  2. 详细分析本机号码一键登录原理

    详细分析本机号码一键登录原理! 很多 APP 的目前都支持「本机号码一键登录」功能.本机号码一键登录是基于运营商独有网关认证能力推出的账号认证产品.用户只需一键授权,即可实现以本机号码注册/登录,相比 ...

  3. linux shell数据重定向(输入重定向与输出重定向)详细分析

    转载自: linux shell数据重定向(输入重定向与输出重定向)详细分析 - 程默 - 博客园 http://www.cnblogs.com/chengmo/archive/2010/10/20/ ...

  4. Blueprint代码详细分析-Android10.0编译系统(七)

    摘要:Blueprint解析Android.bp到ninja的代码流程时如何走的? 阅读本文大约需要花费18分钟. 文章首发微信公众号:IngresGe 专注于Android系统级源码分析,Andro ...

  5. android 串口开发_详细分析Esp8266上电信息打印的数据,如何做到串口通讯上电不乱码打印...

    01 写在前面: 上篇关于如何在内置仅1M的Esp8285做到 OTA 升级的同步到微信公众号,竟然被安信可的某些运维人员看到了,想要转载,我很欣慰,竟然自己的笔记可以被这么大型的公司员工认可! 我是 ...

  6. C语言中的static 详细分析

    google了近三页的关于C语言中static的内容,发现可用的信息很少,要么长篇大论不知所云要么在关键之处几个字略过,对于想挖掘底层原理的初学者来说参考性不是很大.所以,我这篇博文博采众家之长,把互 ...

  7. 【数字信号处理】序列傅里叶变换 ( 序列傅里叶变换定义详细分析 | 证明单位复指数序列正交完备性 | 序列存在傅里叶变换的性质 | 序列绝对可和 → 序列傅里叶变换一定存在 )

    文章目录 一.序列傅里叶变换定义详细分析 二.证明单位复指数序列正交完备性 三.序列存在傅里叶变换的性质 一.序列傅里叶变换定义详细分析 序列傅里叶变换 SFT , 英文全称 " Seque ...

  8. 新手向:从不同的角度来详细分析Redis

    最近对华为云分布式缓存产品Redis做了一些研究,于是整理了一些基本的知识拿出来与大家分享,首先跟大家分享的是,如何从不同的角度来详细使用Redis. 小编将从以下9个角度来进行详细分析,希望可以帮到 ...

  9. C语言中static详细分析

    google了近三页的关于C语言中static的内容,发现可用的信息很少,要么长篇大论不知所云要么在关键之处几个字略过,对于想挖掘底层原理的初学者来说参考性不是很大.所以,我这篇博文博采众家之长,把互 ...

最新文章

  1. oracle rac对心跳要求_关于心跳网络引起的Oracle RAC的节点驱逐(不是实例驱逐)...
  2. 使用FluentValidation来进行数据有效性验证
  3. php调用另一个php文件里的变量的值,thinkphp中一个方法调用另一个步骤的变量
  4. oracle clob 查询换行,sqoop clob从Oracle导入到hive   回车换行导致记录增多
  5. python做的游戏可以导出吗_Python for RenderDoc批量导出模型和贴图
  6. 互联网高级Java面试总结
  7. 如何 SSH 到 Linux 服务器里的特定目录及执行命令?
  8. bmklocationmanager方法没有回调_关于node中的回调(必学)
  9. 剑指offer——面试题6:重建二叉树
  10. C#——orm-FulentData(sqlite3)——异常捕获
  11. 华中师范大学计算机学院在哪个校区,关于《计科,你的力量在哪里?》★华师计算机科学系★研究生情况~~过来人指点下...
  12. c++打开图片查看器并查看图片
  13. 阿里巴巴矢量图标使用
  14. copy和strong的区别
  15. Java常见练习题总结
  16. 2021厦门湖滨中学高考成绩查询,厦门各高中本科上线率2020
  17. ASO学习——《ASO优化大师》笔记
  18. 生信软件(1)bioawk
  19. 介绍一个全局最优化的方法:随机游走算法(Random Walk)
  20. Linux之系统安装读书笔记

热门文章

  1. Mr.Go 会客厅第二期,B站+斗鱼“后浪” Gopher 火花四溅!
  2. 三大思维导图软件比较
  3. 转:7招,教你在工作中高效做笔记
  4. OPPO技术开放日第六期丨OPPO安全解析“应用与数据安全防护”背后的技术
  5. ins无法发帖_instagram发完图片就没了_ins发不了照片_解决方法
  6. Rust 学习3, 枚举,集合
  7. 怎么根据日志分析出 PV 和 UV?
  8. 解放生产力 - Xcode 与 Simulator 技巧整理(持续更新中)
  9. 腾讯投 10 亿在武汉建中部最大研发中心
  10. 增量式编码器有哪些分类?增量式编码器是如何工作的?