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-starter-actuatorSpring Boot框架中的一个模块,它提供了一系列用于监控和管理应用程序的端点(endpoints),比如/health、/info、/metrics等。这些端点可以通过HTTP请求访问,返回应用程序的各种指标和状态信息。 spring-boot-starter-actuator的原理主要包括以下几个方面: 1. 自动配置:Spring Boot框架提供了自动配置功能,可以根据应用程序的依赖项和配置文件来自动配置spring-boot-starter-actuator模块。 2. 端点映射:spring-boot-starter-actuator使用Spring MVC框架来处理HTTP请求。它通过端点映射(Endpoint Mapping)将HTTP请求映射到相应的端点处理器(Endpoint Handler)上。 3. 端点处理器:每个端点都有一个对应的处理器,用于处理HTTP请求并返回响应。端点处理器可以是自定义的Java类,也可以是Spring Boot框架提供的默认实现。 4. 数据源:spring-boot-starter-actuator会从应用程序的各种数据源中收集指标和状态信息,比如JVM内存使用情况、数据库连接池状态等。这些数据源可以是应用程序本身、第三方库、操作系统等。 5. 安全性:为了保护应用程序的安全性,spring-boot-starter-actuator提供了一些安全功能,比如基于角色的访问控制、IP地址过滤等。可以通过配置文件来配置这些安全功能。 总之,spring-boot-starter-actuator通过自动配置、端点映射、端点处理器、数据源和安全性等机制,实现了对应用程序的监控和管理。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值