当前位置:   article > 正文

DispatcherServlet 源码分析

DispatcherServlet 源码分析

一.DispatcherServlet 源码分析

本文仅了解源码内容即可。

1.观察我们的服务启动⽇志:
当Tomcat启动之后, 有⼀个核⼼的类DispatcherServlet, 它来控制程序的执⾏顺序.所有请求都会先进到DispatcherServlet,执⾏doDispatch 调度⽅法. 如果有拦截器, 会先执⾏拦截器 preHandle() ⽅法的代码, 如果 preHandle() 返回true, 继续访问controller中的⽅法. controller当中的⽅法执⾏完毕后,再回过来执⾏ postHandle() afterCompletion() ,返回给DispatcherServlet, 最终给浏览器响应数据.

1.1初始化(了解) 

DispatcherServlet的初始化⽅法 init() 在其⽗类 HttpServletBean 中实现的
主要作⽤是加载 web.xml 中 DispatcherServlet 的 配置, 并调⽤⼦类的初始化
web.xml是web项⽬的配置⽂件,⼀般的web⼯程都会⽤到web.xml来Listener,Filter,Servlet等, Spring框架从3.1版本开始⽀持Servlet3.0, DispatcherServlet, 实现不再使⽤web.xml

init() 具体代码如下:

  1. @Override
  2. public final void init() throws ServletException {
  3. try {
  4. // ServletConfigPropertyValues 是静态内部类,使⽤ ServletConfig 获取
  5. web.xml 中配置的参数
  6. PropertyValues pvs = new
  7. ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
  8. // 使⽤ BeanWrapper 来构造 DispatcherServlet
  9. BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
  10. ResourceLoader resourceLoader = new
  11. ServletContextResourceLoader(getServletContext());
  12. bw.registerCustomEditor(Resource.class, new
  13. ResourceEditor(resourceLoader, getEnvironment()));
  14. initBeanWrapper(bw);
  15. bw.setPropertyValues(pvs, true);
  16. } catch (BeansException ex) {}
  17. // 让⼦类实现的⽅法,这种在⽗类定义在⼦类实现的⽅式叫做模版⽅法模式
  18. initServletBean();
  19. }
HttpServletBean init() 中调⽤了 initServletBean() , 它是在
FrameworkServlet 类中实现的, 主要作⽤是建⽴ WebApplicationContext 容器(有时也称上下⽂), 并加载 SpringMVC 配置⽂件中定义的 Bean到该容器中, 最后将该容器添加到 ServletContext 中
initServletBean() 的具体代码:
  1. /**
  2. * Overridden method of {@link HttpServletBean}, invoked after any bean
  3. properties
  4. * have been set. Creates this servlet's WebApplicationContext.
  5. */
  6. @Override
  7. protected final void initServletBean() throws ServletException {
  8. getServletContext().log("Initializing Spring " + getClass().getSimpleName()
  9. + " '" + getServletName() + "'");
  10. if (logger.isInfoEnabled()) {
  11. logger.info("Initializing Servlet '" + getServletName() + "'");
  12. }
  13. long startTime = System.currentTimeMillis();
  14. try {
  15. //创建ApplicationContext容器
  16. this.webApplicationContext = initWebApplicationContext();
  17. initFrameworkServlet();
  18. }
  19. catch (ServletException | RuntimeException ex) {
  20. logger.error("Context initialization failed", ex);
  21. throw ex;
  22. }
  23. if (logger.isDebugEnabled()) {
  24. String value = this.enableLoggingRequestDetails ?
  25. "shown which may lead to unsafe logging of potentially sensitive
  26. data" :
  27. "masked to prevent unsafe logging of potentially sensitive data";
  28. logger.debug("enableLoggingRequestDetails='" +
  29. this.enableLoggingRequestDetails +
  30. "': request parameters and headers will be " + value);
  31. }
  32. if (logger.isInfoEnabled()) {
  33. logger.info("Completed initialization in " + (System.currentTimeMillis()
  34. - startTime) + " ms");
  35. }
  36. }
此处打印的⽇志, 也正是控制台打印出来的⽇志:

 源码跟踪技巧:

在阅读框架源码的时候, ⼀定要抓住关键点, 找到核⼼流程.
切忌从头到尾⼀⾏⼀⾏代码去看, ⼀个⽅法的去研究, ⼀定要找到关键流程, 抓住关键点, 先在宏观上对整个流程或者整个原理有⼀个认识, 有精⼒再去研究其中的细节

初始化web容器的过程中, 会通过onRefresh 来初始化SpringMVC的容器 

  1. protected WebApplicationContext initWebApplicationContext() {
  2. //...
  3. if (!this.refreshEventReceived) {
  4. //初始化Spring MVC
  5. synchronized (this.onRefreshMonitor) {
  6. onRefresh(wac);
  7. }
  8. }
  9. return wac;
  10. }
  11. @Override
  12. protected void onRefresh(ApplicationContext context) {
  13. initStrategies(context);
  14. }
  15. /**
  16. * Initialize the strategy objects that this servlet uses.
  17. * <p>May be overridden in subclasses in order to initialize further strategy
  18. objects.
  19. */
  20. protected void initStrategies(ApplicationContext context) {
  21. initMultipartResolver(context);
  22. initLocaleResolver(context);
  23. initThemeResolver(context);
  24. initHandlerMappings(context);
  25. initHandlerAdapters(context);
  26. initHandlerExceptionResolvers(context)
  27. initRequestToViewNameTranslator(context);
  28. initViewResolvers(context);
  29. initFlashMapManager(context);
  30. }
在initStrategies()中进⾏9⼤组件的初始化, 如果没有配置相应的组件,就使⽤默认定义的组件(在
DispatcherServlet.properties中有配置默认的策略, ⼤致了解即可)
⽅法initMultipartResolver、initLocaleResolver、init、initRequestToViewNameTranslator、initFlashMapManager的处理⽅式⼏乎都⼀样(1.2.3.7.8,9),从应⽤⽂中取出指定的Bean, 如果没有, 就使⽤默认的.
⽅法initHandlerMappings、initHandlerAdapters、initHandlerExceptionResolvers的处理⽅式⼏乎
都⼀样(4,5,6)

1.初始化⽂件上传解析器MultipartResolver: 从应⽤上下⽂中获取名称为multipartResolver的Bean,如果没有名为multipartResolver的Bean,则没有提供上传⽂件的解析器
2.初始化区域解析器LocaleResolver: 从应⽤上下⽂中获取名称为localeResolver的Bean,如果没有这个Bean,则默认使⽤AcceptHeaderLocaleResolver作为区域解析器
3.初始化主题解析器ThemeResolver: 从应⽤上下⽂中获取名称为themeResolver的Bean,如果没有这个Bean,则默认使⽤FixedThemeResolver作为主题解析器
4.初始化处理器映射器HandlerMappings: 处理器映射器作⽤,1)通过处理器映射器找到对应的处理器适配器,将请求交给适配器处理;2)缓存每个请求地址URL对应的位置(Controller.xxx⽅法);如果在ApplicationContext发现有HandlerMappings,则从ApplicationContext中获取到所有的HandlerMappings,并进⾏排序;如果在ApplicationContext中没有发现有处理器映射器,则默认BeanNameUrlHandlerMapping作为处理器映射器
5.初始化处理器适配器HandlerAdapter: 作⽤是通过调⽤具体的⽅法来处理具体的请求;如果在 ApplicationContext发现有handlerAdapter,则从ApplicationContext中获取到所有的HandlerAdapter,并进⾏排序;如果在ApplicationContext中没有发现处理器适配器,则默认 SimpleControllerHandlerAdapter作为处理器适配器
6.初始化异常处理器解析器HandlerExceptionResolver: 如果在ApplicationContext发现有
handlerExceptionResolver,则从ApplicationContext中获取到所有的HandlerExceptionResolver,并进⾏排序;如果在ApplicationContext中没有发现异常处理器解析器,则不设置异常处理器

7.初始化RequestToViewNameTranslator: 其作⽤是从Request中获取viewName,从ApplicationContext发现有viewNameTranslator的Bean,如果没有,则默认使⽤DefaultRequestToViewNameTranslator
8.初始化视图解析器ViewResolvers: 先从ApplicationContext中获取名为viewResolver的Bean如果没有,则默认InternalResourceViewResolver作为视图解析器
9.初始化FlashMapManager: 其作⽤是⽤于检索和保存FlashMap(保存从⼀个URL重定向到另⼀个URL时的参数信息),从ApplicationContext发现有flashMapManager的Bean,如果没有,则默认使⽤DefaultFlashMapManager

 1.2 处理请求(核心)

DispatcherServlet 接收到请求后, 执⾏doDispatch 调度⽅法, 再将请求转给Controller.
我们来看doDispatch ⽅法的具体实现:
  1. protected void doDispatch(HttpServletRequest request, HttpServletResponse
  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. try {
  9. ModelAndView mv = null;
  10. Exception dispatchException = null;
  11. try {
  12. processedRequest = this.checkMultipart(request);
  13. multipartRequestParsed = processedRequest != request;
  14. //1. 获取执⾏链
  15. //遍历所有的 HandlerMapping 找到与请求对应的Handler
  16. mappedHandler = this.getHandler(processedRequest);
  17. if (mappedHandler == null) {
  18. this.noHandlerFound(processedRequest, response);
  19. return;
  20. }
  21. //2. 获取适配器
  22. //遍历所有的 HandlerAdapter,找到可以处理该 Handler 的
  23. HandlerAdapter
  24. HandlerAdapter ha =
  25. this.getHandlerAdapter(mappedHandler.getHandler());
  26. String method = request.getMethod();
  27. boolean isGet = HttpMethod.GET.matches(method);
  28. if (isGet || HttpMethod.HEAD.matches(method)) {
  29. long lastModified = ha.getLastModified(request,
  30. mappedHandler.getHandler());
  31. if ((new ServletWebRequest(request,
  32. response)).checkNotModified(lastModified) && isGet) {
  33. return;
  34. }
  35. }
  36. //3. 执⾏拦截器preHandle⽅法
  37. if (!mappedHandler.applyPreHandle(processedRequest, response))
  38. {
  39. return;
  40. }
  41. //4. 执⾏⽬标⽅法
  42. mv = ha.handle(processedRequest, response,
  43. mappedHandler.getHandler());
  44. if (asyncManager.isConcurrentHandlingStarted()) {
  45. return;
  46. }
  47. this.applyDefaultViewName(processedRequest, mv);
  48. //5. 执⾏拦截器postHandle⽅法
  49. mappedHandler.applyPostHandle(processedRequest, response, mv);
  50. } catch (Exception var20) {
  51. dispatchException = var20;
  52. } catch (Throwable var21) {
  53. dispatchException = new NestedServletException("Handler
  54. dispatch failed", var21);
  55. }
  56. //6. 处理视图, 处理之后执⾏拦截器afterCompletion⽅法
  57. this.processDispatchResult(processedRequest, response,
  58. mappedHandler, mv, (Exception)dispatchException);
  59. } catch (Exception var22) {
  60. //7. 执⾏拦截器afterCompletion⽅法
  61. this.triggerAfterCompletion(processedRequest, response,
  62. mappedHandler, var22);
  63. } catch (Throwable var23) {
  64. this.triggerAfterCompletion(processedRequest, response,
  65. mappedHandler, new NestedServletException("Handler processing failed", var23));
  66. }
  67. } finally {
  68. if (asyncManager.isConcurrentHandlingStarted()) {
  69. if (mappedHandler != null) {
  70. mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
  71. }
  72. } else if (multipartRequestParsed) {
  73. this.cleanupMultipart(processedRequest);
  74. }
  75. }
  76. }
HandlerAdapter 在 Spring MVC 中使⽤了适配器模式:
适配器模式, 也叫包装器模式. 简单来说就是⽬标类不能直接使⽤, 通过⼀个新类进⾏包装⼀下, 适配调⽤⽅使⽤.
把两个不兼容的接⼝通过⼀定的⽅式使之兼容。
HandlerAdapter 主要⽤于⽀持不同类型的处理器(如 Controller、HttpRequestHandler 或者
Servlet 等),让它们能够适配统⼀的请求处理流程。这样,Spring MVC 可以通过⼀个统⼀的接⼝来处理来⾃各种处理器的请求。
从上述源码可以看出在开始执⾏ Controller 之前,会先调⽤ 预处理⽅法 applyPreHandle,⽽
applyPreHandle ⽅法的实现源码如下:
  1. boolean applyPreHandle(HttpServletRequest request, HttpServletResponse
  2. response) throws Exception {
  3. for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex =
  4. i++) {
  5. // 获取项⽬中使⽤的拦截器 HandlerInterceptor
  6. HandlerInterceptor interceptor =
  7. (HandlerInterceptor)this.interceptorList.get(i);
  8. if (!interceptor.preHandle(request, response, this.handler)) {
  9. this.triggerAfterCompletion(request, response, (Exception)null);
  10. return false;
  11. }
  12. }
  13. return true;
  14. }
在 applyPreHandle 中会获取所有的拦截器 HandlerInterceptor , 并执⾏拦截器中的
preHandle ⽅法,这样就会咱们前⾯定义的拦截器对应上了,如下图所⽰:
如果拦截器返回true, 整个发放就返回true, 继续执⾏后。
如果拦截器返回fasle, 则中断后续操作

 

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/运维做开发/article/detail/942459
推荐阅读
相关标签
  

闽ICP备14008679号