spring boot 源码解析56-actuator请求处理流程(以EnvironmentEndpoint为例)

前言

前面2篇文章介绍了EndpointHandlerMapping.本文就来看一下访问MvcEndpoint的处理流程.

本文以访问/env 为例进行讲解

解析

  1. 因为我们是get请求,因此为调用FrameworkServlet#doGet来进行处理,代码如下:

    protected final void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
    
        processRequest(request, response);
    }

    在该方法中最终调用了DispatcherServlet#doService,而在该方法中最终调用了doDispatch方法

  2. 在该方法中首先调用getHandler方法,获得HandlerExecutionChain.代码如下:

    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        for (HandlerMapping hm : this.handlerMappings) {
            if (logger.isTraceEnabled()) {
                logger.trace(
                        "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
            }
            HandlerExecutionChain handler = hm.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
        return null;
    }

    遍历其持有的HandlerMappings,依次调用其getHandler获得HandlerExecutionChain,如果获得对应的HandlerExecutionChain,则直接返回.如果没有匹配的HandlerExecutionChain的话,则返回null.

    此时, DispatcherServlet 持有的HandlerMapping有8个,如下:

    1. SimpleUrlHandlerMapping
    2. EndpointHandlerMapping
    3. RequestMappingHandlerMapping
    4. BeanNameUrlHandlerMapping
    5. SimpleUrlHandlerMapping
    6. EmptyHandlerMapping
    7. EmptyHandlerMapping
    8. WelcomePageHandlerMapping

    此时最终调用的是EndpointHandlerMapping#getHandler 获得对应的HandlerExecutionChain.代码如下:

    List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);

    接下来调用AbstractHandlerMapping#getHandlerExecutionChain–> 添加Interceptor.

    此时在EndpointHandlerMapping中持有的adaptedInterceptors只有1个SkipPathExtensionContentNegotiation 由于不是MappedInterceptor的实例,因此直接加到HandlerExecutionChain中.代码如下:

    protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
        HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
                (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
    
        String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
        for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
            if (interceptor instanceof MappedInterceptor) {
                MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
                if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                    chain.addInterceptor(mappedInterceptor.getInterceptor());
                }
            }
            else {
                chain.addInterceptor(interceptor);
            }
        }
        return chain;
    }

    由于securityInterceptor不等于null并且不是跨域请求,因此在原先的Interceptor基础上加上securityInterceptor.代码如下:

    private HandlerExecutionChain addSecurityInterceptor(HandlerExecutionChain chain) {
        List<HandlerInterceptor> interceptors = new ArrayList<HandlerInterceptor>();
        if (chain.getInterceptors() != null) {
            interceptors.addAll(Arrays.asList(chain.getInterceptors()));
        }
        interceptors.add(this.securityInterceptor);
        return new HandlerExecutionChain(chain.getHandler(),
                interceptors.toArray(new HandlerInterceptor[interceptors.size()]));
    }

    因此此时Interceptor有2两个–>SkipPathExtensionContentNegotiation,securityInterceptor

  3. 接下来调用DispatcherServlet#getHandlerAdapter获得对应的HandlerAdapter.代码如下:

    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        for (HandlerAdapter ha : this.handlerAdapters) {
            if (logger.isTraceEnabled()) {
                logger.trace("Testing handler adapter [" + ha + "]");
            }
            if (ha.supports(handler)) {
                return ha;
            }
        }
        throw new ServletException("No adapter for handler [" + handler +
                "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
    }

    此时有3个HandlerAdapter:

    1. RequestMappingHandlerAdapter
    2. HttpRequestHandlerAdapter
    3. SimpleControllerHandlerAdapter

    最终返回的是RequestMappingHandlerAdapter.代码如下:

    public final boolean supports(Object handler) {
        return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
    }

    其supportsInternal 返回true.代码如下:

    protected boolean supportsInternal(HandlerMethod handlerMethod) {
        return true;
    }
  4. 接下来执行HandlerExecutionChain#applyPreHandle方法,依次调用其持有的Interceptor的preHandle方法,代码如下:

    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for (int i = 0; i < interceptors.length; i++) {
                HandlerInterceptor interceptor = interceptors[i];
                if (!interceptor.preHandle(request, response, this.handler)) {
                    triggerAfterCompletion(request, response, null);
                    return false;
                }
                this.interceptorIndex = i;
            }
        }
        return true;
    }

    此时持有的是SkipPathExtensionContentNegotiation,MvcEndpointSecurityInterceptor.其实现分别如下:

    1. SkipPathExtensionContentNegotiation,代码如下:

      public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
              Object handler) throws Exception {
          request.setAttribute(SKIP_ATTRIBUTE, Boolean.TRUE);
          return true;
      }
    2. MvcEndpointSecurityInterceptor,代码如下:

      public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
              Object handler) throws Exception {
          // 1. 如果是(cors请求并且是options类型的请求并且请求头Access-Control-Request-Method存在)或者(不进行校验),则直接返回true
          if (CorsUtils.isPreFlightRequest(request) || !this.secure) {
              return true;
          }
          HandlerMethod handlerMethod = (HandlerMethod) handler;
          // 2. 如果是options请求并且HandlerMethod不是MvcEndpoint的实例,则返回true
          if (HttpMethod.OPTIONS.matches(request.getMethod())
                  && !(handlerMethod.getBean() instanceof MvcEndpoint)) {
              return true;
          }
          MvcEndpoint mvcEndpoint = (MvcEndpoint) handlerMethod.getBean();
          // 3. 如果配置的endpoints.sensitive=false或者对应的endpoints.xxx.sensitive=false,则直接返回true
          if (!mvcEndpoint.isSensitive()) {
              return true;
          }
          // 4. 如果拥有相应的权限则返回true
          if (isUserAllowedAccess(request)) {
              return true;
          }
          // 5. 返回401
          sendFailureResponse(request, response);
          return false;
      }

      关于这部分的内容,可以看spring boot 源码解析55-spring boot actuator HandlerMapping全网独家揭秘

  5. 接下来调用HandlerAdapter#handle,最终调用了EnvironmentEndpoint#invoke方法.这部分的调用链如下:

    --> org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(HttpServletRequest, HttpServletResponse, Object)
        -->     org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(HttpServletRequest, HttpServletResponse, HandlerMethod)
            -->     org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(HttpServletRequest, HttpServletResponse, HandlerMethod)
                -->     org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletWebRequest, ModelAndViewContainer, Object...)
                    --> org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(NativeWebRequest, ModelAndViewContainer, Object...)
                        --> org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(Object...)
                            --> org.springframework.boot.actuate.endpoint.EnvironmentEndpoint.invoke() // 最终执行到
  6. 后续的渲染,就不必要贴出了,因为最重要的调用逻辑已经讲明了.后面的处理都是通用的,读者可以自行阅读.

    原文作者:Spring Boot
    原文地址: https://blog.csdn.net/qq_26000415/article/details/79231959
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞