• 什么,这年头还有人不知道404


    写在前面

    哥,来帮我看看,这个请求怎么404了,明明接口路径是对的啊!一个下午,组里的小哥突然让我帮忙看这个问题,我不禁一惊,啥,这年头了还有人搞不定404,如果还有,那一定是没看完这篇文章!

    一、为何要写这篇文章

    作为一名crud工程师,咱们的工作真的就只剩增删改查了吗?在笔者所遇到各类从事软件开发的人群中,工作1-2年甚至3-5年的,在遇到404这类的http异常code时都显得束手无策,经验稍微丰富的点“老”手可能凭经验能看出问题出在哪里,但是又有多少人知道为什么会出现404 code,往深了说,又有多少人知道一个http请求是如何找到controller中的方法并执行呢?更进一步,在你了解到spring mvc 的处理机制前,如果让你来设计这套流程,你会怎么做?

    二、举个例子

    下面是一个最简单的http接口例子

    接口路径为 /api/common/getNumber

    1. @RequestMapping("/api/common")
    2. @Controller
    3. public class CommonController {
    4. @RequestMapping("/getNumber")
    5. @ResponseBody
    6. public Object getNumberMethod(@RequestParam("range") Integer range) {
    7. return ThreadLocalRandom.current().nextInt(range);
    8. }
    9. }

    过滤器

    1. public class LogFilter implements Filter {
    2. @Override
    3. public void init(FilterConfig filterConfig) {
    4. }
    5. @Override
    6. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    7. HttpServletRequest request = (HttpServletRequest) servletRequest;
    8. log.info("经过logFilter ==== {}", request);
    9. filterChain.doFilter(servletRequest, servletResponse);
    10. }
    11. @Override
    12. public void destroy() {
    13. }
    14. }

    拦截器

    1. public class LogInterceptor extends HandlerInterceptorAdapter {
    2. @Override
    3. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    4. log.info("经过拦截器 === {}", request);
    5. return true;
    6. }
    7. }

    执行结果

    1. 2023-10-03 19:22:12.373 INFO 54072 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
    2. 2023-10-03 19:22:12.373 INFO 54072 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
    3. 2023-10-03 19:22:15.646 INFO 54072 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 3273 ms
    4. 2023-10-03 19:22:19.759 INFO 54072 --- [nio-8080-exec-1] com.example.demo.filter.LogFilter : 经过logFilter ==== org.apache.catalina.connector.RequestFacade@67d0b80a
    5. 2023-10-03 19:22:26.177 INFO 54072 --- [nio-8080-exec-1] c.e.demo.intercpetor.LogInterceptor : 经过拦截器 === org.apache.catalina.connector.RequestFacade@67d0b80a

    三、执行过程

    1、运行环境

    jdk 1.8

    spring-boot-starter-parent 2.1.9.RELEASE

    spring-webmvc 5.1.0

    2、源码解析

    在进行源码解析时,我们先过一眼整个请求处理的过程时序图

    0)ApplicationFilterChain # internalDoFilter(ServletRequest request, ServletResponse response)

    该方法是tomcat包中的方法,用来执行filter,在filter执行完成后再执行servlet.service方法,而servlet.service方法也是业务的入口方法。servlet对象即为DispatchServlet,其service对应的也就是其父类HttpServlet的service方法。

    1. private void internalDoFilter(ServletRequest request,
    2. ServletResponse response)
    3. throws IOException, ServletException {
    4. // n 表示filter数的总和,pos表示当前位置
    5. // Call the next filter if there is one
    6. if (pos < n) { // 如果filter没有执行完成,则走下面的逻辑继续执行
    7. // 获取pos位置对应的filterConfig,同时将pos+1
    8. ApplicationFilterConfig filterConfig = filters[pos++];
    9. try {
    10. Filter filter = filterConfig.getFilter();
    11. if (request.isAsyncSupported() && "false".equalsIgnoreCase(
    12. filterConfig.getFilterDef().getAsyncSupported())) {
    13. request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
    14. }
    15. if( Globals.IS_SECURITY_ENABLED ) {
    16. final ServletRequest req = request;
    17. final ServletResponse res = response;
    18. Principal principal =
    19. ((HttpServletRequest) req).getUserPrincipal();
    20. Object[] args = new Object[]{req, res, this};
    21. SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
    22. } else {
    23. // 执行具体的filter逻辑,如本文例子中的LogFilter
    24. filter.doFilter(request, response, this);
    25. }
    26. } catch (IOException | ServletException | RuntimeException e) {
    27. throw e;
    28. } catch (Throwable e) {
    29. e = ExceptionUtils.unwrapInvocationTargetException(e);
    30. ExceptionUtils.handleThrowable(e);
    31. throw new ServletException(sm.getString("filterChain.filter"), e);
    32. }
    33. return;
    34. }
    35. // We fell off the end of the chain -- call the servlet instance
    36. // 如果执行完最后一个filter
    37. try {
    38. if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
    39. lastServicedRequest.set(request);
    40. lastServicedResponse.set(response);
    41. }
    42. if (request.isAsyncSupported() && !servletSupportsAsync) {
    43. request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
    44. Boolean.FALSE);
    45. }
    46. // Use potentially wrapped request from this point
    47. if ((request instanceof HttpServletRequest) &&
    48. (response instanceof HttpServletResponse) &&
    49. Globals.IS_SECURITY_ENABLED ) {
    50. final ServletRequest req = request;
    51. final ServletResponse res = response;
    52. Principal principal =
    53. ((HttpServletRequest) req).getUserPrincipal();
    54. Object[] args = new Object[]{req, res};
    55. SecurityUtil.doAsPrivilege("service",
    56. servlet,
    57. classTypeUsedInService,
    58. args,
    59. principal);
    60. } else {
    61. // 执行HttpServlet的service方法
    62. servlet.service(request, response);
    63. }
    64. } catch (IOException | ServletException | RuntimeException e) {
    65. throw e;
    66. } catch (Throwable e) {
    67. e = ExceptionUtils.unwrapInvocationTargetException(e);
    68. ExceptionUtils.handleThrowable(e);
    69. throw new ServletException(sm.getString("filterChain.servlet"), e);
    70. } finally {
    71. if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
    72. lastServicedRequest.set(null);
    73. lastServicedResponse.set(null);
    74. }
    75. }
    76. }

    1)FrameworkServlet # service(HttpServletRequest request, HttpServletResponse response)

    由于HttpServlet的service方法只是做了入参的转换,即将ServletRequest转成HttpServletRequest,ServletResponse转成HttpServletResponse,参数转换完后随即调用了子类 FrameworkServlet 的service(HttpServletRequest request, HttpServletResponse response) 方法,而FrameworkServlet 的service方法功能很简单,就是为了适配httpMethod 中的 PATCH模式,非PATCH模式直接走父类HttpServlet的service(HttpServletRequest request, HttpServletResponse response)方法【DispatcherServlet的继承关系依赖图如下】。

    1. /**
    2. * Override the parent class implementation in order to intercept PATCH requests.
    3. * 主要是为了拦截 httpMethod 中的 PATCH
    4. *
    5. * patch是2010后成为的正式http方法,详见RFC5789,
    6. * 它是对put的补充,在没有patch之前,我们都是用put进行更新操作,
    7. * 这时候我们的接口中通常会有一个逻辑规则,如:如果对象的的一个字符属性为NULL,
    8. * 那么就是不更新该属性(字段)值,如果对象的字符属性是“”,那么就更新该属性(字段)的值,
    9. * 通过这种方式来避免全部覆盖的操作。现在有了patch就解决了这种判断,在put接口中不管属性是不是null,
    10. * 都进行更新,在patch接口中就对非null的进行更新
    11. *
    12. */
    13. @Override
    14. protected void service(HttpServletRequest request, HttpServletResponse response)
    15. throws ServletException, IOException {
    16. HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
    17. // 如果是 HttpMethod.PATCH 或者 找不到httpMethod
    18. if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
    19. processRequest(request, response);
    20. }
    21. else {
    22. // 其他情况则调用父类也就是HttpServlet的service方法,
    23. // 在这里,由于我们的请求是get类型的,所以会走到此分支
    24. super.service(request, response);
    25. }
    26. }

    2)HttpServlet # service(HttpServletRequest req, HttpServletResponse resp)

    进入HttpServlet的service(HttpServletRequest req, HttpServletResponse resp)方法后,该方法主要做了method类型的区分调用,即get post put等对应的doGet,doPost,doPut 由子类实现。

    1. /**
    2. * Receives standard HTTP requests from the public
    3. * service method and dispatches
    4. * them to the doMethod methods defined in
    5. * this class. This method is an HTTP-specific version of the
    6. * {@link javax.servlet.Servlet#service} method. There's no
    7. * need to override this method.
    8. *
    9. * @param req the {@link HttpServletRequest} object that
    10. * contains the request the client made of
    11. * the servlet
    12. *
    13. * @param resp the {@link HttpServletResponse} object that
    14. * contains the response the servlet returns
    15. * to the client
    16. *
    17. * @exception IOException if an input or output error occurs
    18. * while the servlet is handling the
    19. * HTTP request
    20. *
    21. * @exception ServletException if the HTTP request
    22. * cannot be handled
    23. *
    24. * @see javax.servlet.Servlet#service
    25. */
    26. protected void service(HttpServletRequest req, HttpServletResponse resp)
    27. throws ServletException, IOException {
    28. String method = req.getMethod();
    29. if (method.equals(METHOD_GET)) {
    30. // 默认返回 -1
    31. long lastModified = getLastModified(req);
    32. if (lastModified == -1) {
    33. // servlet doesn't support if-modified-since, no reason
    34. // to go through further expensive logic
    35. doGet(req, resp);
    36. } else {
    37. long ifModifiedSince;
    38. try {
    39. ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
    40. } catch (IllegalArgumentException iae) {
    41. // Invalid date header - proceed as if none was set
    42. ifModifiedSince = -1;
    43. }
    44. if (ifModifiedSince < (lastModified / 1000 * 1000)) {
    45. // If the servlet mod time is later, call doGet()
    46. // Round down to the nearest second for a proper compare
    47. // A ifModifiedSince of -1 will always be less
    48. maybeSetLastModified(resp, lastModified);
    49. doGet(req, resp);
    50. } else {
    51. resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
    52. }
    53. }
    54. } else if (method.equals(METHOD_HEAD)) {
    55. long lastModified = getLastModified(req);
    56. maybeSetLastModified(resp, lastModified);
    57. doHead(req, resp);
    58. } else if (method.equals(METHOD_POST)) {
    59. doPost(req, resp);
    60. } else if (method.equals(METHOD_PUT)) {
    61. doPut(req, resp);
    62. } else if (method.equals(METHOD_DELETE)) {
    63. doDelete(req, resp);
    64. } else if (method.equals(METHOD_OPTIONS)) {
    65. doOptions(req,resp);
    66. } else if (method.equals(METHOD_TRACE)) {
    67. doTrace(req,resp);
    68. } else {
    69. //
    70. // Note that this means NO servlet supports whatever
    71. // method was requested, anywhere on this server.
    72. //
    73. String errMsg = lStrings.getString("http.method_not_implemented");
    74. Object[] errArgs = new Object[1];
    75. errArgs[0] = method;
    76. errMsg = MessageFormat.format(errMsg, errArgs);
    77. resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
    78. }
    79. }

    3)DispatcherServlet # doDispatch(HttpServletRequest request, HttpServletResponse response)

    HttpServlet的service(HttpServletRequest req, HttpServletResponse resp)方法经过一连串的包装调用后就会进入最重要的DispatcherServlet 的 doDispatch(HttpServletRequest request, HttpServletResponse response)方法,doDispatch顾名思义就是将请求进行分发,包括获取HandlerExecutionChain,执行拦截器,获取执行器适配器,handler调用,视图渲染等工作。

    1. /**
    2. * Process the actual dispatching to the handler.
    3. *

      The handler will be obtained by applying the servlet's HandlerMappings in order.

    4. * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
    5. * to find the first that supports the handler class.
    6. *

      All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers

    7. * themselves to decide which methods are acceptable.
    8. * @param request current HTTP request
    9. * @param response current HTTP response
    10. * @throws Exception in case of any kind of processing failure
    11. */
    12. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    13. HttpServletRequest processedRequest = request;
    14. HandlerExecutionChain mappedHandler = null;
    15. boolean multipartRequestParsed = false;
    16. WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    17. try {
    18. ModelAndView mv = null;
    19. Exception dispatchException = null;
    20. try {
    21. processedRequest = checkMultipart(request);
    22. multipartRequestParsed = (processedRequest != request);
    23. // Determine handler for the current request.
    24. // 该方法得到一个 HandlerExecutionChain 处理器执行链,实际上它包含了一个真正的处理handler
    25. // 和 若干个拦截器
    26. mappedHandler = getHandler(processedRequest);
    27. if (mappedHandler == null) {
    28. noHandlerFound(processedRequest, response);
    29. return;
    30. }
    31. // Determine handler adapter for the current request.
    32. // 获取执行器适配器
    33. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    34. // Process last-modified header, if supported by the handler.
    35. String method = request.getMethod();
    36. boolean isGet = "GET".equals(method);
    37. if (isGet || "HEAD".equals(method)) {
    38. long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
    39. if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
    40. return;
    41. }
    42. }
    43. // 执行拦截器的preHandle方法,如果拦截了则直接返回
    44. if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    45. return;
    46. }
    47. // Actually invoke the handler.
    48. // 真正调用handler
    49. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    50. if (asyncManager.isConcurrentHandlingStarted()) {
    51. return;
    52. }
    53. applyDefaultViewName(processedRequest, mv);
    54. // 执行拦截器的 postHandle 方法
    55. mappedHandler.applyPostHandle(processedRequest, response, mv);
    56. }
    57. catch (Exception ex) {
    58. dispatchException = ex;
    59. }
    60. catch (Throwable err) {
    61. // As of 4.3, we're processing Errors thrown from handler methods as well,
    62. // making them available for @ExceptionHandler methods and other scenarios.
    63. dispatchException = new NestedServletException("Handler dispatch failed", err);
    64. }
    65. // 处理视图的方法,将逻辑视图转为物理视图的过程,同时执行拦截器的afterCompletion方法
    66. processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    67. }
    68. catch (Exception ex) {
    69. // 执行拦截器的afterCompletion方法
    70. triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    71. }
    72. catch (Throwable err) {
    73. triggerAfterCompletion(processedRequest, response, mappedHandler,
    74. new NestedServletException("Handler processing failed", err));
    75. }
    76. finally {
    77. if (asyncManager.isConcurrentHandlingStarted()) {
    78. // Instead of postHandle and afterCompletion
    79. if (mappedHandler != null) {
    80. mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
    81. }
    82. }
    83. else {
    84. // Clean up any resources used by a multipart request.
    85. if (multipartRequestParsed) {
    86. cleanupMultipart(processedRequest);
    87. }
    88. }
    89. }
    90. }
    a. DispatcherServlet # getHandler(HttpServletRequest request)

    该方法得到一个 HandlerExecutionChain 处理器执行链,实际上它包含了一个真正的处理handler和 若干个拦截器

    1. /**
    2. * Return the HandlerExecutionChain for this request.
    3. *

      Tries all handler mappings in order.

    4. * @param request current HTTP request
    5. * @return the HandlerExecutionChain, or {@code null} if no handler could be found
    6. */
    7. @Nullable
    8. protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    9. if (this.handlerMappings != null) {
    10. for (HandlerMapping mapping : this.handlerMappings) {
    11. HandlerExecutionChain handler = mapping.getHandler(request);
    12. if (handler != null) {
    13. return handler;
    14. }
    15. }
    16. }
    17. return null;
    18. }

    从上图执行过程中可以看出,HandlerExecutionChain的获取主要依赖于HandlerMapping ,那么何为HandlerMapping?HandlerMapping 称为处理器映射器,

    从HandlerMapping的继承关系图中可以看出,HandlerMapping可以大致分为 AbstractHandlerMethodMapping 和 AbstractUrlHandlerMapping 两大类,其中AbstractHandlerMethodMapping 映射器主要处理用 @Controller@RequestMapping 这样注解来描述视图控制器的逻辑,也是我们日常开发中用的最多的场景;而AbstractUrlHandlerMapping用的比较少,比如:<mvc:view-controller path="" view-name=""/> 标签配置资源不经过视图控制器直接跳转就用到了 SimpleUrlHandlerMapping 这种映射器。

    当执行 HandlerExecutionChain handler = mapping.getHandler(request); 时,会跳到 AbstractHandlerMapping类,执行getHandler方法,AbstractHandlerMapping 是个抽象类,提供了模板方法,主要的功能在代码块getHandlerInternal方法,在本例中getHandlerInternal的功能主要就是根据request来获取HandlerMethod,HandlerMethod对象存储于MappingRegistry的mappingLookup映射表中,该映射表在容器启动时,探测类上是否有Controller或者RequestMapping注解修饰,如有则生成RequestMappingInfo到HandlerMethod的映射关系。

    1. /**
    2. * Look up a handler for the given request, falling back to the default
    3. * handler if no specific one is found.
    4. * @param request current HTTP request
    5. * @return the corresponding handler instance, or the default handler
    6. * @see #getHandlerInternal
    7. */
    8. @Override
    9. @Nullable
    10. public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    11. // 重要, 本例中返回 HandlerMethod 对象,该对象里面包含了目标类的目标method信息以及目标类的bean
    12. Object handler = getHandlerInternal(request);
    13. if (handler == null) {
    14. handler = getDefaultHandler();
    15. }
    16. if (handler == null) {
    17. return null;
    18. }
    19. // Bean name or resolved handler?
    20. if (handler instanceof String) {
    21. String handlerName = (String) handler;
    22. handler = obtainApplicationContext().getBean(handlerName);
    23. }
    24. // 将 handler(本例中对应的是HandlerMethod 对象)以及拦截器信息封装到 HandlerExecutionChain 链中
    25. HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
    26. if (logger.isTraceEnabled()) {
    27. logger.trace("Mapped to " + handler);
    28. }
    29. else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
    30. logger.debug("Mapped to " + executionChain.getHandler());
    31. }
    32. if (CorsUtils.isCorsRequest(request)) {
    33. CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
    34. CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
    35. CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
    36. executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
    37. }
    38. return executionChain;
    39. }
    b. DispatcherServlet # getHandlerAdapter(Object handler)

    该方法是获取处理器适配器,那么为什么要有处理器适配器,直接执行handler不行吗?原因就是处理器 handler 的类型是 Object 类型。Spring 中的handler实现多变,比如用户的处理器可以实现 Controller 接口或者 HttpRequestHandler 接口,也可以用 @RequestMapping 注解将方法作为一个处理器等,这就导致 Spring MVC 无法直接执行这个处理器。所以这里需要一个处理器适配器,由它去执行处理。获取处理器适配的方法寥寥数语,最主要的逻辑就是 adapter.supports(handler),根据语句猜测大概就是根据条件匹配对应的适配器。在我们弄清楚这个逻辑前,先来看看第一条语句if (this.handlerAdapters != null),那么这个this.handlerAdapters 的值从哪里来?

    1. /**
    2. * Return the HandlerAdapter for this handler object.
    3. * @param handler the handler object to find an adapter for
    4. * @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
    5. */
    6. protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    7. if (this.handlerAdapters != null) {
    8. for (HandlerAdapter adapter : this.handlerAdapters) {
    9. if (adapter.supports(handler)) {
    10. return adapter;
    11. }
    12. }
    13. }
    14. throw new ServletException("No adapter for handler [" + handler +
    15. "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
    16. }

    根据下图的执行过程可以看出,有三个满足条件的handlerAdapter。

    那么,这三个handlerAdapter是如何确定的呢?如下:

    1. /**
    2. * Initialize the HandlerAdapters used by this class.
    3. *

      If no HandlerAdapter beans are defined in the BeanFactory for this namespace,

    4. * we default to SimpleControllerHandlerAdapter.
    5. */
    6. private void initHandlerAdapters(ApplicationContext context) {
    7. this.handlerAdapters = null;
    8. if (this.detectAllHandlerAdapters) {
    9. // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
    10. Map matchingBeans =
    11. BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
    12. if (!matchingBeans.isEmpty()) {
    13. this.handlerAdapters = new ArrayList<>(matchingBeans.values());
    14. // We keep HandlerAdapters in sorted order.
    15. AnnotationAwareOrderComparator.sort(this.handlerAdapters);
    16. }
    17. }
    18. else {
    19. try {
    20. HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
    21. this.handlerAdapters = Collections.singletonList(ha);
    22. }
    23. catch (NoSuchBeanDefinitionException ex) {
    24. // Ignore, we'll add a default HandlerAdapter later.
    25. }
    26. }
    27. // Ensure we have at least some HandlerAdapters, by registering
    28. // default HandlerAdapters if no other adapters are found.
    29. if (this.handlerAdapters == null) {
    30. this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
    31. if (logger.isTraceEnabled()) {
    32. logger.trace("No HandlerAdapters declared for servlet '" + getServletName() +
    33. "': using default strategies from DispatcherServlet.properties");
    34. }
    35. }
    36. }

    上述代码的大体含义如下:

    1. 如果“开启”探测功能,则扫描已注册的 HandlerAdapter 的 Bean 们,添加到 handlerAdapters 中,默认 开启 ,这里会进行排序,可以通过实现 Order 接口设置排序值

    2. 如果“关闭”探测功能,则获得 Bean 名称为 "handlerAdapter" 对应的 Bean ,将其添加至 handlerAdapters

    3. 如果未获得到,则获得默认配置的 HandlerAdapter 类,调用 getDefaultStrategies(ApplicationContext context, Class strategyInterface) 方法,就是从 DispatcherServlet.properties 文件中读取 HandlerAdapter 的默认实现类,如下:

    那么回到getHandlerAdapter方法中的adapter.supports(handler)语句,依次通过boolean supports(Object handler)方法判断使用哪个adapter。HandlerAdapter即采用适配器模式, 用于统一不同handler的接口调用。在本文例子中,最后采用的是RequestMappingHandlerAdapter,其对应的supports方法是

    1. /**
    2. * This implementation expects the handler to be an {@link HandlerMethod}.
    3. * @param handler the handler instance to check
    4. * @return whether or not this adapter can adapt the given handler
    5. */
    6. @Override
    7. public final boolean supports(Object handler) {
    8. return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
    9. }
    1. /**
    2. * Always return {@code true} since any method argument and return value
    3. * type will be processed in some way. A method argument not recognized
    4. * by any HandlerMethodArgumentResolver is interpreted as a request parameter
    5. * if it is a simple type, or as a model attribute otherwise. A return value
    6. * not recognized by any HandlerMethodReturnValueHandler will be interpreted
    7. * as a model attribute.
    8. */
    9. @Override
    10. protected boolean supportsInternal(HandlerMethod handlerMethod) {
    11. return true;
    12. }
    c. AbstractHandlerMethodAdapter # handle(HttpServletRequest request, HttpServletResponse response, Object handler)

    该方法就是最终要执行业务方法,也就是Controller类中的某个方法的入口。

    1. /**
    2. * This implementation expects the handler to be an {@link HandlerMethod}.
    3. */
    4. @Override
    5. @Nullable
    6. public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
    7. throws Exception {
    8. return handleInternal(request, response, (HandlerMethod) handler);
    9. }

    handleInternal 方法依赖于子类的实现

    1. @Override
    2. protected ModelAndView handleInternal(HttpServletRequest request,
    3. HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    4. ModelAndView mav;
    5. checkRequest(request);
    6. // Execute invokeHandlerMethod in synchronized block if required.
    7. if (this.synchronizeOnSession) {
    8. HttpSession session = request.getSession(false);
    9. if (session != null) {
    10. Object mutex = WebUtils.getSessionMutex(session);
    11. synchronized (mutex) {
    12. mav = invokeHandlerMethod(request, response, handlerMethod);
    13. }
    14. }
    15. else {
    16. // No HttpSession available -> no mutex necessary
    17. mav = invokeHandlerMethod(request, response, handlerMethod);
    18. }
    19. }
    20. else {
    21. // No synchronization on session demanded at all...
    22. mav = invokeHandlerMethod(request, response, handlerMethod);
    23. }
    24. if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
    25. if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
    26. applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
    27. }
    28. else {
    29. prepareResponse(response);
    30. }
    31. }
    32. return mav;
    33. }

    在以上方法中,我们只需要关注invokeHandlerMethod(request, response, handlerMethod) 即可,接着看:

    1. /**
    2. * Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
    3. * if view resolution is required.
    4. * @since 4.2
    5. * @see #createInvocableHandlerMethod(HandlerMethod)
    6. */
    7. @Nullable
    8. protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
    9. HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    10. ServletWebRequest webRequest = new ServletWebRequest(request, response);
    11. try {
    12. WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
    13. ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
    14. ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
    15. .... 省去若干代码
    16. // 只需关注这行即可
    17. invocableMethod.invokeAndHandle(webRequest, mavContainer);
    18. if (asyncManager.isConcurrentHandlingStarted()) {
    19. return null;
    20. }
    21. return getModelAndView(mavContainer, modelFactory, webRequest);
    22. }
    23. finally {
    24. webRequest.requestCompleted();
    25. }
    26. }
    1. /**
    2. * Invoke the method and handle the return value through one of the
    3. * configured {@link HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers}.
    4. * @param webRequest the current request
    5. * @param mavContainer the ModelAndViewContainer for this request
    6. * @param providedArgs "given" arguments matched by type (not resolved)
    7. */
    8. public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
    9. Object... providedArgs) throws Exception {
    10. // 实际调用
    11. Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    12. // 处理结果状态值
    13. setResponseStatus(webRequest);
    14. if (returnValue == null) {
    15. if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
    16. disableContentCachingIfNecessary(webRequest);
    17. mavContainer.setRequestHandled(true);
    18. return;
    19. }
    20. }
    21. else if (StringUtils.hasText(getResponseStatusReason())) {
    22. mavContainer.setRequestHandled(true);
    23. return;
    24. }
    25. mavContainer.setRequestHandled(false);
    26. Assert.state(this.returnValueHandlers != null, "No return value handlers");
    27. try {
    28. // 处理返回值
    29. this.returnValueHandlers.handleReturnValue(
    30. returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    31. }
    32. catch (Exception ex) {
    33. if (logger.isTraceEnabled()) {
    34. logger.trace(formatErrorForReturnValue(returnValue), ex);
    35. }
    36. throw ex;
    37. }
    38. }
    1. /**
    2. * Invoke the method after resolving its argument values in the context of the given request.
    3. *

      Argument values are commonly resolved through

    4. * {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}.
    5. * The {@code providedArgs} parameter however may supply argument values to be used directly,
    6. * i.e. without argument resolution. Examples of provided argument values include a
    7. * {@link WebDataBinder}, a {@link SessionStatus}, or a thrown exception instance.
    8. * Provided argument values are checked before argument resolvers.
    9. *

      Delegates to {@link #getMethodArgumentValues} and calls {@link #doInvoke} with the

    10. * resolved arguments.
    11. * @param request the current request
    12. * @param mavContainer the ModelAndViewContainer for this request
    13. * @param providedArgs "given" arguments matched by type, not resolved
    14. * @return the raw value returned by the invoked method
    15. * @throws Exception raised if no suitable argument resolver can be found,
    16. * or if the method raised an exception
    17. * @see #getMethodArgumentValues
    18. * @see #doInvoke
    19. */
    20. @Nullable
    21. public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
    22. Object... providedArgs) throws Exception {
    23. // 解析参数值
    24. Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    25. if (logger.isTraceEnabled()) {
    26. logger.trace("Arguments: " + Arrays.toString(args));
    27. }
    28. // 执行调用
    29. return doInvoke(args);
    30. }
    1. /**
    2. * Invoke the handler method with the given argument values.
    3. */
    4. @Nullable
    5. protected Object doInvoke(Object... args) throws Exception {
    6. // 改变方法的可见性,这就是为什么即使controller中的方法是private的也能正常访问
    7. ReflectionUtils.makeAccessible(getBridgedMethod());
    8. try {
    9. // 这就是精髓所在,熟悉的配方,熟悉的味道,这不就是反射调用吗!!!
    10. return getBridgedMethod().invoke(getBean(), args);
    11. }
    12. catch (IllegalArgumentException ex) {
    13. assertTargetBean(getBridgedMethod(), getBean(), args);
    14. String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
    15. throw new IllegalStateException(formatInvokeError(text, args), ex);
    16. }
    17. catch (InvocationTargetException ex) {
    18. // Unwrap for HandlerExceptionResolvers ...
    19. Throwable targetException = ex.getTargetException();
    20. if (targetException instanceof RuntimeException) {
    21. throw (RuntimeException) targetException;
    22. }
    23. else if (targetException instanceof Error) {
    24. throw (Error) targetException;
    25. }
    26. else if (targetException instanceof Exception) {
    27. throw (Exception) targetException;
    28. }
    29. else {
    30. throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
    31. }
    32. }
    33. }
    d. DispatchServlet # processDispatchResult

    对于某些接口需要渲染ModelAndView的,需要在下面这个方法里处理,例如,有个接口采用的是thymeleaf模板引擎来渲染接口数据。如下例子

    1. @RequestMapping("/testHtml")
    2. public String testHtml(Map map) {
    3. map.put("msg","

      Hello,SpringBoot

      "
      );
    4. map.put("users", Arrays.asList("zhangsan","lisi"));
    5. return "testHtml";
    6. }
    1. html>
    2. <html lang="en" xmlns:th="http://www.thymeleaf.org">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>测试title>head>
    6. <body><h1>测试页面h1>
    7. <div th:text="${msg}">div>
    8. <div th:utext="${msg}">div>
    9. <h4 th:each="user :${users}"
    10. th:text="${user}">
    11. h4>
    12. body>
    13. html>

    效果如下:

    针对上面这个例子,执行完testHtml方法后,拿到返回的ModelAndView对象后执行下面processDispatchResult中的render方法渲染页面信息

    1. /**
    2. * Handle the result of handler selection and handler invocation, which is
    3. * either a ModelAndView or an Exception to be resolved to a ModelAndView.
    4. */
    5. private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
    6. @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
    7. @Nullable Exception exception) throws Exception {
    8. boolean errorView = false;
    9. ... 省略若干代码
    10. // Did the handler return a view to render?
    11. if (mv != null && !mv.wasCleared()) {
    12. // 渲染 ModelAndView
    13. render(mv, request, response);
    14. if (errorView) {
    15. WebUtils.clearErrorRequestAttributes(request);
    16. }
    17. }
    18. else {
    19. if (logger.isTraceEnabled()) {
    20. logger.trace("No view rendering, null ModelAndView returned.");
    21. }
    22. }
    23. if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
    24. // Concurrent handling started during a forward
    25. return;
    26. }
    27. if (mappedHandler != null) {
    28. // 执行拦截器的afterCompletion方法
    29. mappedHandler.triggerAfterCompletion(request, response, null);
    30. }
    31. }

    从下图中可以看到 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 返回的是mv 非空,说明有对应的ModelAndView需要渲染。

    3、总结

    从以上的执行过程来看,一个完整的http get 请求大概会经过执行 filter、从HandlerMapping中获取HandlerExecutionChain,HandlerExecutionChain里面包含了一个真正的处理handler(HandlerMethod,HandlerMethod包含了要执行方法的method信息以及类实例对象) 和若干个拦截器interceptors,然后根据handler获取对应的HandlerAdapter去执行,在执行过程中通过反射机制调用对应Controller的方法拿到结果,拿到结果后进行返回值的回写以及页面的渲染(如果有必要),在执行过程的前后会分别执行接口的拦截器preHandle以及postHandle方法。

    那么,这整个过程的示意图如下

    四、用到的技术点

    1、设计模式

    1) 模版模式 HandlerMapping

    比如 HandlerMapping的实现抽象类AbstractHandlerMapping中有个getHanlder 方法,其中getHandlerInternal定义了模版方法,具体由子类实现

    1. /**
    2. * Look up a handler for the given request, falling back to the default
    3. * handler if no specific one is found.
    4. * @param request current HTTP request
    5. * @return the corresponding handler instance, or the default handler
    6. * @see #getHandlerInternal
    7. */
    8. @Override
    9. @Nullable
    10. public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    11. // 该方法则是模版方法,具体由子类实现
    12. Object handler = getHandlerInternal(request);
    13. if (handler == null) {
    14. handler = getDefaultHandler();
    15. }
    16. if (handler == null) {
    17. return null;
    18. }
    19. // Bean name or resolved handler?
    20. if (handler instanceof String) {
    21. String handlerName = (String) handler;
    22. handler = obtainApplicationContext().getBean(handlerName);
    23. }
    24. HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
    25. if (logger.isTraceEnabled()) {
    26. logger.trace("Mapped to " + handler);
    27. }
    28. else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
    29. logger.debug("Mapped to " + executionChain.getHandler());
    30. }
    31. if (CorsUtils.isCorsRequest(request)) {
    32. CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
    33. CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
    34. CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
    35. executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
    36. }
    37. return executionChain;
    38. }
    1. /**
    2. * Look up a handler for the given request, returning {@code null} if no
    3. * specific one is found. This method is called by {@link #getHandler};
    4. * a {@code null} return value will lead to the default handler, if one is set.
    5. *

      On CORS pre-flight requests this method should return a match not for

    6. * the pre-flight request but for the expected actual request based on the URL
    7. * path, the HTTP methods from the "Access-Control-Request-Method" header, and
    8. * the headers from the "Access-Control-Request-Headers" header thus allowing
    9. * the CORS configuration to be obtained via {@link #getCorsConfiguration(Object, HttpServletRequest)},
    10. *

      Note: This method may also return a pre-built {@link HandlerExecutionChain},

    11. * combining a handler object with dynamically determined interceptors.
    12. * Statically specified interceptors will get merged into such an existing chain.
    13. * @param request current HTTP request
    14. * @return the corresponding handler instance, or {@code null} if none found
    15. * @throws Exception if there is an internal error
    16. */
    17. @Nullable
    18. protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;

    2) 责任链模式 Filter

    http请求中会执行filter, filter采用的是责任链模式,整个过程沿着链条上的各个有序的filter执行

    1. /**
    2. * ApplicationFilterChain
    3. */public void doFilter(ServletRequest request, ServletResponse response)
    4. throws IOException, ServletException {
    5. ... 省略若干行
    6. internalDoFilter(request,response);
    7. }
    8. private void internalDoFilter(ServletRequest request,
    9. ServletResponse response)
    10. throws IOException, ServletException {
    11. // Call the next filter if there is one
    12. if (pos < n) {
    13. ApplicationFilterConfig filterConfig = filters[pos++];
    14. ...省略若干行
    15. // 调用filter的doFilter方法,同时将 this对象传过去,方便将责任链传递下去
    16. filter.doFilter(request, response, this);
    17. return;
    18. }
    19. }
    1. @Slf4j
    2. public class LogFilter implements Filter {
    3. @Override
    4. public void init(FilterConfig filterConfig) {
    5. }
    6. @Override
    7. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    8. HttpServletRequest request = (HttpServletRequest) servletRequest;
    9. log.info("经过logFilter ==== {}", request);
    10. // 接收上一个filter传过来的filterChain,同时调用filterChain的doFilter方法
    11. filterChain.doFilter(servletRequest, servletResponse);
    12. }
    13. @Override
    14. public void destroy() {
    15. }
    16. }

    2、反射

    在执行handler时,handler处理过程中,会把流量转发到各个controller中的方法执行,为了统一调用逻辑,这里采用了反射的方式处理

    1. /**
    2. * Invoke the handler method with the given argument values.
    3. */
    4. @Nullable
    5. protected Object doInvoke(Object... args) throws Exception {
    6. // 改变方法的可见性,这就是为什么即使controller中的方法是private的也能正常访问
    7. ReflectionUtils.makeAccessible(getBridgedMethod());
    8. try {
    9. // 这就是精髓所在,熟悉的配方,熟悉的味道,这不就是反射调用吗!!!
    10. return getBridgedMethod().invoke(getBean(), args);
    11. }
    12. catch (IllegalArgumentException ex) {
    13. assertTargetBean(getBridgedMethod(), getBean(), args);
    14. String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
    15. throw new IllegalStateException(formatInvokeError(text, args), ex);
    16. }
    17. catch (InvocationTargetException ex) {
    18. // Unwrap for HandlerExceptionResolvers ...
    19. Throwable targetException = ex.getTargetException();
    20. if (targetException instanceof RuntimeException) {
    21. throw (RuntimeException) targetException;
    22. }
    23. else if (targetException instanceof Error) {
    24. throw (Error) targetException;
    25. }
    26. else if (targetException instanceof Exception) {
    27. throw (Exception) targetException;
    28. }
    29. else {
    30. throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
    31. }
    32. }
    33. }

    五、回顾

    那么回过头来你能回答文章最前面提出的问题了吗?

    1、http请求出现404等状态码时,知道从哪里开始排查了吗?

    2、一个http请求是如何找到controller中的方法并执行呢?

    3、在你了解到spring mvc 的处理机制前,如果让你来设计这套流程,你会怎么做?

    针对前面三个问题,在你了解spring mvc 的处理机制后,你觉得这个流程设计到怎么样呢?

  • 相关阅读:
    php一维数组合并
    IDEA指定Maven settings file文件未生效
    【算法 | 模拟No.2】leetcode495. 提莫攻击
    【编程之路】面试必刷TOP101:动态规划(72-77,Python实现)
    基于Java+微信小程序实现《模拟考试平台》
    Java学习——基本语法笔记
    Postman之CSV或JOSN文件实现数据驱动
    你不能错过的【Python爬虫】测试3(爬取所有内容 + 完整源代码 + 架构 + 结果)
    去掉WordPress网页图片默认链接功能
    2022年全国研究生数学建模竞赛华为杯D题PISA架构芯片资源排布问题求解全过程文档及程序
  • 原文地址:https://blog.csdn.net/cow__sky/article/details/133621384