通过源码看SpringMVC的DispatcherServlet

配置SpringMVC的应用的过程和使用这在这篇博客中Spring MVC应用已经配置好了。那么,前端控制器DispatcherServlet截获请求后做了什么工作呢?DispatcherServlet又是如何分派请求的呢?
A994ln.jpg
代码位置: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. Map<String, Object>attributesSnapshot=null;

  14. if (WebUtils.isIncludeRequest(request)){

  15. attributesSnapshot=new HashMap<String, Object>();

  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)处理分发请求得到的数据模型和视图的渲染。