[关闭]
@vonzhou 2016-08-24T13:45:18.000000Z 字数 15318 阅读 243

DispatcherServlet 源码阅读(1)

博客


有时间还是应该多看看源码。

DispatcherServlet 是一个实实在在的 Servlet,所以 Spring MVC 引入后不会改变 Servlet 容器的行为,仍然是解析 web.xml 部署文件,只需要在里面配置这个 Servlet 即可。
比如下面配置 dispatcher Servlet 处理所有的请求,也体现了 DispatcherServlet 是前端控制器(Front Controller)。contextConfigLocation 上下文参数用于配置路径的指定,如果没有的话就使用默认的值。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app xmlns="http://java.sun.com/xml/ns/javaee"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
  5. version="3.0">
  6. <servlet>
  7. <servlet-name>dispatcher</servlet-name>
  8. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  9. <load-on-startup>1</load-on-startup>
  10. </servlet>
  11. <servlet-mapping>
  12. <servlet-name>dispatcher</servlet-name>
  13. <url-pattern>/</url-pattern>
  14. </servlet-mapping>
  15. <listener>
  16. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  17. </listener>
  18. <context-param>
  19. <param-name>contextConfigLocation</param-name>
  20. <param-value>
  21. /WEB-INF/dispatcher-servlet.xml
  22. classpath:service-context.xml
  23. </param-value>
  24. </context-param>
  25. </web-app>

1.JPG-33.2kB

DispatcherServlet 初始化

DispatcherServlet 的父类 HttpServletBean 覆盖了 HttpServlet 的 init 方法,实现该 servlet 的初始化。

  1. /**
  2. * Map config parameters onto bean properties of this servlet, and
  3. * invoke subclass initialization.
  4. * @throws ServletException if bean properties are invalid (or required
  5. * properties are missing), or if subclass initialization fails.
  6. */
  7. @Override
  8. public final void init() throws ServletException {
  9. if (logger.isDebugEnabled()) {
  10. logger.debug("Initializing servlet '" + getServletName() + "'");
  11. }
  12. // Set bean properties from init parameters.
  13. try {
  14. PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
  15. BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
  16. ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
  17. bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
  18. initBeanWrapper(bw);
  19. bw.setPropertyValues(pvs, true);
  20. }
  21. catch (BeansException ex) {
  22. logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
  23. throw ex;
  24. }
  25. // Let subclasses do whatever initialization they like.
  26. initServletBean();
  27. if (logger.isDebugEnabled()) {
  28. logger.debug("Servlet '" + getServletName() + "' configured successfully");
  29. }
  30. }

正如注释所说 initServletBean() 留由子类实现,体现了模板方法模式,当上述bean属性设置完成后,进入这里 FrameworkServlet#init() 创建 Servlet 的上下文 WebApplicationContext,initWebApplicationContext 首先会获得该 Web 应用的 root WebApplicationContext (通常是由 org.springframework.web.context.ContextLoaderListener 加载的),然后根据这个根上下文得到我们这个 Servlet 的 WebApplicationContext。initFrameworkServlet 方法是空的,而且子类 DispatcherServlet 也没有覆盖。

  1. /**
  2. * Overridden method of {@link HttpServletBean}, invoked after any bean properties
  3. * have been set. Creates this servlet's WebApplicationContext.
  4. */
  5. @Override
  6. protected final void initServletBean() throws ServletException {
  7. getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
  8. if (this.logger.isInfoEnabled()) {
  9. this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
  10. }
  11. long startTime = System.currentTimeMillis();
  12. try {
  13. this.webApplicationContext = initWebApplicationContext();
  14. initFrameworkServlet();
  15. }
  16. catch (ServletException ex) {
  17. this.logger.error("Context initialization failed", ex);
  18. throw ex;
  19. }
  20. catch (RuntimeException ex) {
  21. this.logger.error("Context initialization failed", ex);
  22. throw ex;
  23. }
  24. if (this.logger.isInfoEnabled()) {
  25. long elapsedTime = System.currentTimeMillis() - startTime;
  26. this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
  27. elapsedTime + " ms");
  28. }
  29. }
  30. /**
  31. * Initialize and publish the WebApplicationContext for this servlet.
  32. * <p>Delegates to {@link #createWebApplicationContext} for actual creation
  33. * of the context. Can be overridden in subclasses.
  34. * @return the WebApplicationContext instance
  35. * @see #FrameworkServlet(WebApplicationContext)
  36. * @see #setContextClass
  37. * @see #setContextConfigLocation
  38. */
  39. protected WebApplicationContext initWebApplicationContext() {
  40. WebApplicationContext rootContext =
  41. WebApplicationContextUtils.getWebApplicationContext(getServletContext());
  42. WebApplicationContext wac = null;
  43. if (this.webApplicationContext != null) {
  44. // A context instance was injected at construction time -> use it
  45. wac = this.webApplicationContext;
  46. if (wac instanceof ConfigurableWebApplicationContext) {
  47. ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
  48. if (!cwac.isActive()) {
  49. // The context has not yet been refreshed -> provide services such as
  50. // setting the parent context, setting the application context id, etc
  51. if (cwac.getParent() == null) {
  52. // The context instance was injected without an explicit parent -> set
  53. // the root application context (if any; may be null) as the parent
  54. cwac.setParent(rootContext);
  55. }
  56. configureAndRefreshWebApplicationContext(cwac);
  57. }
  58. }
  59. }
  60. if (wac == null) {
  61. // No context instance was injected at construction time -> see if one
  62. // has been registered in the servlet context. If one exists, it is assumed
  63. // that the parent context (if any) has already been set and that the
  64. // user has performed any initialization such as setting the context id
  65. wac = findWebApplicationContext();
  66. }
  67. if (wac == null) {
  68. // No context instance is defined for this servlet -> create a local one
  69. wac = createWebApplicationContext(rootContext);
  70. }
  71. if (!this.refreshEventReceived) {
  72. // Either the context is not a ConfigurableApplicationContext with refresh
  73. // support or the context injected at construction time had already been
  74. // refreshed -> trigger initial onRefresh manually here.
  75. onRefresh(wac);
  76. }
  77. if (this.publishContext) {
  78. // Publish the context as a servlet context attribute.
  79. String attrName = getServletContextAttributeName();
  80. getServletContext().setAttribute(attrName, wac);
  81. if (this.logger.isDebugEnabled()) {
  82. this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
  83. "' as ServletContext attribute with name [" + attrName + "]");
  84. }
  85. }
  86. return wac;
  87. }

DispatcherServlet 处理请求流程

FrameworkServlet 中覆盖了 HttpServlet 的 doGet(),doPost()等方法,而 doGet(),doPost()等又直接调用方法 processRequest 来处理请求,代码如下。

  1. /**
  2. * Delegate GET requests to processRequest/doService.
  3. * <p>Will also be invoked by HttpServlet's default implementation of {@code doHead},
  4. * with a {@code NoBodyResponse} that just captures the content length.
  5. * @see #doService
  6. * @see #doHead
  7. */
  8. @Override
  9. protected final void doGet(HttpServletRequest request, HttpServletResponse response)
  10. throws ServletException, IOException {
  11. processRequest(request, response);
  12. }
  13. /**
  14. * Delegate POST requests to {@link #processRequest}.
  15. * @see #doService
  16. */
  17. @Override
  18. protected final void doPost(HttpServletRequest request, HttpServletResponse response)
  19. throws ServletException, IOException {
  20. processRequest(request, response);
  21. }

然后我们进入 processRequest 方法,实际的请求处理是调用其抽象方法 doService。

  1. /**
  2. * Process this request, publishing an event regardless of the outcome.
  3. * <p>The actual event handling is performed by the abstract
  4. * {@link #doService} template method.
  5. */
  6. protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
  7. throws ServletException, IOException {
  8. long startTime = System.currentTimeMillis();
  9. Throwable failureCause = null;
  10. LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
  11. LocaleContext localeContext = buildLocaleContext(request);
  12. RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
  13. ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
  14. WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
  15. asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
  16. initContextHolders(request, localeContext, requestAttributes);
  17. try {
  18. doService(request, response);
  19. }
  20. catch (ServletException ex) {
  21. failureCause = ex;
  22. throw ex;
  23. }
  24. catch (IOException ex) {
  25. failureCause = ex;
  26. throw ex;
  27. }
  28. catch (Throwable ex) {
  29. failureCause = ex;
  30. throw new NestedServletException("Request processing failed", ex);
  31. }
  32. finally {
  33. resetContextHolders(request, previousLocaleContext, previousAttributes);
  34. if (requestAttributes != null) {
  35. requestAttributes.requestCompleted();
  36. }
  37. if (logger.isDebugEnabled()) {
  38. if (failureCause != null) {
  39. this.logger.debug("Could not complete request", failureCause);
  40. }
  41. else {
  42. if (asyncManager.isConcurrentHandlingStarted()) {
  43. logger.debug("Leaving response open for concurrent processing");
  44. }
  45. else {
  46. this.logger.debug("Successfully completed request");
  47. }
  48. }
  49. }
  50. publishRequestHandledEvent(request, response, startTime, failureCause);
  51. }
  52. }
  53. /**
  54. * Subclasses must implement this method to do the work of request handling,
  55. * receiving a centralized callback for GET, POST, PUT and DELETE.
  56. * <p>The contract is essentially the same as that for the commonly overridden
  57. * {@code doGet} or {@code doPost} methods of HttpServlet.
  58. * <p>This class intercepts calls to ensure that exception handling and
  59. * event publication takes place.
  60. * @param request current HTTP request
  61. * @param response current HTTP response
  62. * @throws Exception in case of any kind of processing failure
  63. * @see javax.servlet.http.HttpServlet#doGet
  64. * @see javax.servlet.http.HttpServlet#doPost
  65. */
  66. protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
  67. throws Exception;

然后在 DispatcherServlet 中具体实现请求的处理分发,先是把一些资源放到请求属性中,然后调用 doDispatch 实现请求分发到控制器的 handler。doDispatch 中首先会判断是否是文件传输流的请求(利用MultipartResolver),如果是的话就会转为 MultipartHttpServletRequest。接下来 getHandler(processedRequest) 根据请求获得对应的handler,最后调用 handle() 处理请求,会反射到在控制器中实现的方法。

  1. /**
  2. * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
  3. * for the actual dispatching.
  4. */
  5. @Override
  6. protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
  7. if (logger.isDebugEnabled()) {
  8. String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
  9. logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
  10. " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
  11. }
  12. // Keep a snapshot of the request attributes in case of an include,
  13. // to be able to restore the original attributes after the include.
  14. Map<String, Object> attributesSnapshot = null;
  15. if (WebUtils.isIncludeRequest(request)) {
  16. attributesSnapshot = new HashMap<String, Object>();
  17. Enumeration<?> attrNames = request.getAttributeNames();
  18. while (attrNames.hasMoreElements()) {
  19. String attrName = (String) attrNames.nextElement();
  20. if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
  21. attributesSnapshot.put(attrName, request.getAttribute(attrName));
  22. }
  23. }
  24. }
  25. // Make framework objects available to handlers and view objects.
  26. request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
  27. request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
  28. request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
  29. request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
  30. FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
  31. if (inputFlashMap != null) {
  32. request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
  33. }
  34. request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
  35. request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
  36. try {
  37. doDispatch(request, response);
  38. }
  39. finally {
  40. if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
  41. // Restore the original attribute snapshot, in case of an include.
  42. if (attributesSnapshot != null) {
  43. restoreAttributesAfterInclude(request, attributesSnapshot);
  44. }
  45. }
  46. }
  47. }
  48. /**
  49. * Process the actual dispatching to the handler.
  50. * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
  51. * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
  52. * to find the first that supports the handler class.
  53. * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
  54. * themselves to decide which methods are acceptable.
  55. * @param request current HTTP request
  56. * @param response current HTTP response
  57. * @throws Exception in case of any kind of processing failure
  58. */
  59. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  60. HttpServletRequest processedRequest = request;
  61. HandlerExecutionChain mappedHandler = null;
  62. boolean multipartRequestParsed = false;
  63. WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
  64. try {
  65. ModelAndView mv = null;
  66. Exception dispatchException = null;
  67. try {
  68. processedRequest = checkMultipart(request);
  69. multipartRequestParsed = (processedRequest != request);
  70. // Determine handler for the current request.
  71. mappedHandler = getHandler(processedRequest);
  72. if (mappedHandler == null || mappedHandler.getHandler() == null) {
  73. noHandlerFound(processedRequest, response);
  74. return;
  75. }
  76. // Determine handler adapter for the current request.
  77. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
  78. // Process last-modified header, if supported by the handler.
  79. String method = request.getMethod();
  80. boolean isGet = "GET".equals(method);
  81. if (isGet || "HEAD".equals(method)) {
  82. long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
  83. if (logger.isDebugEnabled()) {
  84. logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
  85. }
  86. if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
  87. return;
  88. }
  89. }
  90. if (!mappedHandler.applyPreHandle(processedRequest, response)) {
  91. return;
  92. }
  93. // Actually invoke the handler.
  94. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  95. if (asyncManager.isConcurrentHandlingStarted()) {
  96. return;
  97. }
  98. applyDefaultViewName(processedRequest, mv);
  99. mappedHandler.applyPostHandle(processedRequest, response, mv);
  100. }
  101. catch (Exception ex) {
  102. dispatchException = ex;
  103. }
  104. processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
  105. }
  106. catch (Exception ex) {
  107. triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
  108. }
  109. catch (Error err) {
  110. triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
  111. }
  112. finally {
  113. if (asyncManager.isConcurrentHandlingStarted()) {
  114. // Instead of postHandle and afterCompletion
  115. if (mappedHandler != null) {
  116. mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
  117. }
  118. }
  119. else {
  120. // Clean up any resources used by a multipart request.
  121. if (multipartRequestParsed) {
  122. cleanupMultipart(processedRequest);
  123. }
  124. }
  125. }
  126. }

HandlerAdapter接口的handle方法抽象的是一个handler如何处理一个请求,该接口实现有下面几个。

2.jpg-19.6kB

AnnotationMethodHandlerAdapter 基于HTTP请求的路径,方法,请求参数,使用 RequestMapping 注解来映handler,从 Spring 3.2建议使用RequestMappingHandlerAdapter。这里看看AnnotationMethodHandlerAdapter是如何实现handle方法的, 使用了反射,最后通过 invokeHandlerMethod 执行了对应的handler方法。

  1. @Override
  2. public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
  3. throws Exception {
  4. Class<?> clazz = ClassUtils.getUserClass(handler);
  5. Boolean annotatedWithSessionAttributes = this.sessionAnnotatedClassesCache.get(clazz);
  6. if (annotatedWithSessionAttributes == null) {
  7. annotatedWithSessionAttributes = (AnnotationUtils.findAnnotation(clazz, SessionAttributes.class) != null);
  8. this.sessionAnnotatedClassesCache.put(clazz, annotatedWithSessionAttributes);
  9. }
  10. if (annotatedWithSessionAttributes) {
  11. checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
  12. }
  13. else {
  14. checkAndPrepare(request, response, true);
  15. }
  16. // Execute invokeHandlerMethod in synchronized block if required.
  17. if (this.synchronizeOnSession) {
  18. HttpSession session = request.getSession(false);
  19. if (session != null) {
  20. Object mutex = WebUtils.getSessionMutex(session);
  21. synchronized (mutex) {
  22. return invokeHandlerMethod(request, response, handler);
  23. }
  24. }
  25. }
  26. return invokeHandlerMethod(request, response, handler);
  27. }
  28. protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
  29. throws Exception {
  30. ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
  31. Method handlerMethod = methodResolver.resolveHandlerMethod(request);
  32. ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
  33. ServletWebRequest webRequest = new ServletWebRequest(request, response);
  34. ExtendedModelMap implicitModel = new BindingAwareModelMap();
  35. Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
  36. ModelAndView mav =
  37. methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
  38. methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
  39. return mav;
  40. }

小结

不要满足于得心应手的事,要不断的做触及能力之上的东西,才会成长。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注