通过源码看SpringMVC的DispatcherServlet

配置SpringMVC的应用的过程和使用这在这篇博客中Spring MVC应用已经配置好了。那么,前端控制器DispatcherServlet截获请求后做了什么工作呢?DispatcherServlet又是如何分派请求的呢?

代码位置:orglspringframeworklweb/servIetIDispatcherServlet.java

第一步初始化组件

  1. protected void initStrategies(ApplicationContextcontext){
  2. initMultipartResolver(context);//初始化上传文件解析器
  3. initLocaleResolver(context);//初始化本地化解析器
  4. initThemeResolver(context);//初始化主题解析器
  5. initHandlerMappings(context),//初始化处理器映射器,将请求映射到处理器
  6. initHandlerAdapters(context);//初始化处理器适配器
  7. initHandlerExceptionResolvers(context);
  8. //初始化处理器异常解析器,如果执行过程中遇到异常将交给HandlerExceptionResolver来解析
  9. initRequestTOViewNameTranslator(conext);//初始化请求到视图名称解析器
  10. initViewResolvers(context);
  11. //初始化视图解析器,通过ViewResolver解析逻辑视图名到具体视图实现
  12. initFlashMapManager(context);//初始化flash映射管理器
  13. }

initStrategies方法将在WebApplicationContext初始化后自动执行,自动扫描上下文的Bean。根据名称或类型匹配的机制查找自定义的组件,如果没有找到则会装配一套Spring的默认组件。在org.springframeworlc.web.servlet路径下有一个DispatcherServlet.properties配置文件,该文件指定了DispatcherServlet所使用的默认组件。
当然开发者希望使用自定义类型的组件,有时候也是必须的,因为上传文件组件是默认不安装的,我们再Spring配置文件中配置自定义的Bean组件即可。Spring MVC如果发现上下文中有用户自定义的组件,就不会使用默认的组件,如:。

文件上传解析器。只允许一个实例
(1)查找名为muliipartResolver、类型为MuliipartResolver的Bean作为该类型组件。
(2)如果用户没有在上下文中显式定义MuliipartResolver类型的组件,则DispatcherServlet将不会加载该类型的组件。
(3)所以我们要用到时要配置这种实例,可以看这篇文章springMVC和springBoot使用MultipartFile上传文件

第二步,服务过程
一般来说Servlet有一个服务方法doService来为Http清求提供服务,DispatcherServlet也是如此,它的doService方法如下:

  1. @Override
  2. prctected void doService (HttpServletRequestrequestHttpServietResponse
  3. response) throws Exception{
  4. if(logger.isDebugEnabled()){
  5. String resumed=WebAsyncUtils.getAsyncManager(request).hasConcurrentResult()?
  6. resumed":"";
  7. logger.debug("DispatcherServiet with name”,+getServletName()+"'"+resumed+
  8. "processing"+request.getMethod()+"request for [" +qetRequestUri(request)"+"]");
  9. }
  10. //快照处理
  11. //Keep a snapshot of the requestattributes in case of an include
  12. //to be able to rest。rethe original attributes after the include
  13. MapattributesSnapshot=null;
  14. if (WebUtils.isIncludeRequest(request)){
  15. attributesSnapshot=new HashMap();
  16. Enumeration attrNames=request.getAttributeNames();
  17. while (attrNames.hasMoreElements()){
  18. String attrName=(String) attrNames .nextElement();
  19. if (this.cleanupAfterinclude
  20. attrName.startsWith("org.springframework.web.servlet”)){
  21. attributesSnapshot.put(attrNamerequest.getAttribute (attrName));
  22. }
  23. )
  24. //Make framework objects available to handlers and view objects.
  25. //设置Web IOC容器
  26. request.setAttribute(WEB APPLICATION CONTEXT ATTRIBUTE,getWebApplicationContext());
  27. //设置国际化属性
  28. request.setAttribute(LOCALE RESOLVER ATTRIBUTEthis.localeResolver);
  29. //主题属性
  30. request.setAttribute(THEME RESOLVER ATTRIBUTE,七his.themeResolver);
  31. //主题源属性
  32. ....
  33. try{
  34. //处理分发
  35. doDispatch(requestresponse);
  36. }
  37. finally{
  38. if(!WebAsyncUtils.getAsyncManaqer(request) .isConcurrentHandimgStarted()){
  39. //Restore the original attribute snapshot,in case of an include.
  40. if (attributesSnapshot!=null){
  41. restoreAttributesAfterinclude(requestattributesSnapshot);
  42. }
  43. }
  44. }
  45. }

从这段源码中我们看到了快照的处理,使用快照可以更快响应用户请求,并且设置些属书!最后所有的流程都集中在了doDispatch方法中。在Spring MVC流程中这是一个最为重要的方法,因此我们还需要探索它,如:

  1. protected void doDispatch(HttpServletRequest requestHttpServletResponse
  2. response)throws Exception{
  3. HttpServletRequest processedRequest=request;
  4. HandlerExecutionChain mappedHandler=null;
  5. boolean multipartRequestParsed=false;
  6. WebAsyncManager asyncManager=WebAsyncUtils .getAsyncManager(request);
  7. try{
  8. //模型和视图
  9. ModelAndView my=null;
  10. //错误处理
  11. Exception dispatchException=null;
  12. try{
  13. //文件上传处理解析器
  14. processedRequest=checkMultipart(request);
  15. //是否为文件上传请求
  16. multipartRequestParsed=(processedRequest!=request);
  17. //Determine handler for the current request.
  18. //获取匹配的执行链
  19. mappedHandler=getHandler(processedRequest);
  20. //没有处理器错误
  21. if (mappedHandler==null || mappedHandler .getHandler()==null){
  22. noHandlerFound(processedRequestresponse)
  23. return;
  24. }
  25. //找到对应的处理适配器(HandlerAdapter)
  26. HandlerAdapter ha =getHandlerAdapter(mappedHandler.getHandler());
  27. //判断是http的get方法还是post方法
  28. String method=request.getMethod();
  29. boolean isGet="GET".equals(method);
  30. //GET方法的处理
  31. if(isGet ||"HEAD".equals(method)){
  32. long hastModified=ha .getLastModified(requestmappedHandler .getHandler());
  33. if(logger .isDebugEnabled()){
  34. logger.debug("Last-Modifie value for ["+getRequestUri(request)+"]iS:"+lastModified);
  35. if(new ServletWebRequest(request,response).checkNotModified(lastModified)&&isGet){
  36. return;}}
  37. //执行拦截器的事前方法,如果返回false,则流程结束
  38. if(!mappedHandler.applyPreHandle(processedRequestresponse)){
  39. return;
  40. }
  41. //执行处理器,返回ModelAndView
  42. mv =ha .handle (processedRequest,response,mappedHandler.getHandler())
  43. if(asyncManager.isConcurrentHandlingStarted()){
  44. return;
  45. }
  46. //如果视图为空,给子设置默认视图的名称
  47. applyDefaultViewName(processedRequest,mv);
  48. //执行处理器拦截器的事后方法
  49. mappedHandler.applyPostHandle(processedRequest,responce,mv);
  50. catch (Exception ex)
  51. //记录异常
  52. dispatchException=eX;
  53. }
  54. catch (Throwable err)
  55. //记录异常
  56. dispatchException=new NestedServletException("Handler dispatch failed"err);
  57. }

通过源码可以看出其流程是:

(1)通过清求找到对应的执行链,执行链包含了拦截器和开发者控制器。
(2)通过处理器找到对应的适配器。
(3)执行拦截器的事前方法,如果返回false,则流程结束,不再处理。
(4)通过适配器运行处理器,然后返回模型和视图。
(5)如果视图没有名称,则给出默认的视图名称。
(6)执行拦截器的事后方法。
(7)处理分发请求得到的数据模型和视图的渲染。