spring boot 源码解析55-spring boot actuator HandlerMapping全网独家揭秘

前言

本文我们来介绍一下EndpointHandlerMapping. 我们在项目中加入spring-boot-starter-actuator 之后,就可以访问如下端点:

path描述是否默认敏感
/shutdown优雅关闭true
/dump打印出线程的堆栈信息. true
/configprops显示出所有的@ConfigurationPropertiestrue
/info显示出任意的应用信息 false
/env暴露出ConfigurableEnvironment的所有的properties true
/health显示应用的健康信息 false
/mappings显示@RequestMapping的所有路径 true
/autoconfig显示出所有的自动装配的结果 true
/metrics显示出当前应用的所有metrics的信息 true
/trace显示出trace的信息(默认只显示100) true
/auditevents/td>显示出所有的audit events true
/heapdump/td>dump堆内存 true
/beans/td>展示所有的bean true
/loggers/td>展示或者修改loggers的配置 true
/logfile/td>展示logfile的内容 true

我们之前解析了一系列的xxxEndpoint,xxxEndpoint是不能直接通过http请求来处理的,因此需要在其上包装一层,因此出现了2个分支–>xxxMvcEndpoint,EndpointMvcAdapter的一系列的子类.但是光这样还不够,我们需要将其注册上去才能在spring mvc 的体系中处理,那么EndpointHandlerMapping就是干这件事的

解析

AbstractEndpointHandlerMapping

AbstractEndpointHandlerMapping 继承自RequestMappingHandlerMapping

  1. 字段,构造器如下:

    // MVC端点集合
    private final Set<E> endpoints;
    
    // 安全处理程序拦截器
    private HandlerInterceptor securityInterceptor;
    
    // CORS配置
    private final CorsConfiguration corsConfiguration;
    
    // 端点的映射路径前缀
    private String prefix = "";
    
    private boolean disabled = false;
    
    
    public AbstractEndpointHandlerMapping(Collection<? extends E> endpoints) {
        this(endpoints, null);
    }
    
    
    public AbstractEndpointHandlerMapping(Collection<? extends E> endpoints,
            CorsConfiguration corsConfiguration) {
        this.endpoints = new HashSet<E>(endpoints);
        // 2. 扩展点
        postProcessEndpoints(this.endpoints);
        this.corsConfiguration = corsConfiguration;
        // By default the static resource handler mapping is LOWEST_PRECEDENCE - 1
        // and the RequestMappingHandlerMapping is 0 (we ideally want to be before both)
        // 默认情况下,静态资源处理程序映射的顺序是 LOWEST_PRECEDENCE - 1 
        setOrder(-100);
        // 不进行后缀匹配
        setUseSuffixPatternMatch(false);
    }

    在构造器中调用了postProcessEndpoints,默认空实现,代码如下:

    protected void postProcessEndpoints(Set<E> endpoints) {
    }
  2. 覆写了如下方法:

    1. afterPropertiesSet,代码如下:

      public void afterPropertiesSet() {
          super.afterPropertiesSet();
          if (!this.disabled) { // 如果端点是启用的 
              for (MvcEndpoint endpoint : this.endpoints) {
                  // 调用AbstractHandlerMethodMapping中的detectHandlerMethods方法进行注册handler,寻找在MvcEndpoint有@RequestMapping注解的方法
                  detectHandlerMethods(endpoint);
              }
          }
      }
      1. 调用父类的afterPropertiesSet
      2. 如果disabled等于true,则遍历endpoints,依次调用detectHandlerMethods,进行注册handler,寻找在MvcEndpoint有@RequestMapping注解的方法.此时就将xxxMvcEndpoint,EndpointMvcAdapter的一系列的子类被@ActuatorGetMapping注解的方法进行了注册
    2. isHandler–>因为所有的handler都已在afterPropertiesSet中进行了处理,因此这里就不需要判断了,直接返回false.如下:

      protected boolean isHandler(Class<?> beanType) {
          return false;
      }
    3. registerHandlerMethod–> 复写了AbstractHandlerMethodMapping#detectHandlerMethods.修改了handler的pattern.代码如下:

      protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) { if (mapping == null) { return; } String[] patterns = getPatterns(handler, mapping);
          if (!ObjectUtils.isEmpty(patterns)) { // 重新注册了映射规则 super.registerHandlerMethod(handler, method, withNewPatterns(mapping, patterns)); }
      }
      1. 如果RequestMappingInfo等于null,则直接return
      2. 获得对应的pattern.代码如下:

        private String[] getPatterns(Object handler, RequestMappingInfo mapping) {
            if (handler instanceof String) {
                handler = getApplicationContext().getBean((String) handler);
            }
            Assert.state(handler instanceof MvcEndpoint, "Only MvcEndpoints are supported");
            String path = getPath((MvcEndpoint) handler); // 获得MvcEndpoint配置的path
            return (path == null ? null : getEndpointPatterns(path, mapping));
        }
        1. 如果handler是String的实例,则将其看做bean id 获得对应的handler
        2. 获得MvcEndpoint配置的path.代码如下:

          protected String getPath(MvcEndpoint endpoint) {
              return endpoint.getPath();
          }
        3. 前缀处理.代码如下:

          private String[] getEndpointPatterns(String path, RequestMappingInfo mapping) {
              // 1. 路径模式前缀
              String patternPrefix = StringUtils.hasText(this.prefix) ? this.prefix + path
                      : path;
              // 2. 根据RequestMappingInfo 获得匹配的路径
              Set<String> defaultPatterns = mapping.getPatternsCondition().getPatterns();
              // 3. 如果defaultPatterns为空
              if (defaultPatterns.isEmpty()) {
                  return new String[] { patternPrefix, patternPrefix + ".json" };
              }
              // 4. 如果不为空,则加defaultPatterns的路径前加上前缀
              List<String> patterns = new ArrayList<String>(defaultPatterns);
              for (int i = 0; i < patterns.size(); i++) {
                  patterns.set(i, patternPrefix + patterns.get(i));
              }
              return patterns.toArray(new String[patterns.size()]);
          }
          1. 路径模式前缀
          2. 根据RequestMappingInfo 获得匹配的路径
          3. 如果defaultPatterns为空,则直接返回patternPrefix,patternPrefix.json
          4. 如果不为空,则加defaultPatterns的路径前加上前缀

          拿MetricsMvcEndpoint来说,其继承自EndpointMvcAdapter,内部持有的是MetricsEndpoint.由于默认情况下,我们没有management.context-path,因此在第1步返回的是/metrics

          由于在MetricsMvcEndpoint中有两个被@ActuatorGetMapping注解的方法:

          1. 声明在EndpointMvcAdapter中的invoke方法,如下:

            @Override
            @ActuatorGetMapping
            @ResponseBody
            public Object invoke() {
                return super.invoke();
            }

            由于没有配置@ActuatorGetMapping中的value属性,因此,也就是在第2步中没有获取到defaultPatterns,因此,最终在第3步返回/metrics,/metrics.json 然后进行注册.

          2. value方法,声明在MetricsMvcEndpoint中,代码如下:

            @ActuatorGetMapping("/{name:.*}")
            @ResponseBody
            @HypermediaDisabled
            public Object value(@PathVariable String name) {
                    ....
            }

            由于配置@ActuatorGetMapping中的value属性–>/{name:.*},因此,在第2步中获取到defaultPatterns–>/{name:.*},因此,最终在第4步返回/metrics/{name:.*}, 然后进行注册.

            因此我们在启动应用时,会看到如下日志:

            2018-02-01 15:24:52.984  INFO 7106 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/metrics/{name:.*}],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint.value(java.lang.String)
            2018-02-01 15:24:52.984  INFO 7106 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/metrics || /metrics.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
      3. 重新注册映射规则–>在注册hanler时重新修改了RequestMappingInfo的mapping.代码如下:

        private RequestMappingInfo withNewPatterns(RequestMappingInfo mapping,
                String[] patternStrings) {
            PatternsRequestCondition patterns = new PatternsRequestCondition(patternStrings,
                    null, null, useSuffixPatternMatch(), useTrailingSlashMatch(), null);
            return new RequestMappingInfo(patterns, mapping.getMethodsCondition(),
                    mapping.getParamsCondition(), mapping.getHeadersCondition(),
                    mapping.getConsumesCondition(), mapping.getProducesCondition(),
                    mapping.getCustomCondition());
        }
    4. getHandlerExecutionChain–>获取处理程序执行链,在DispatcherServlet#doDispatch中会调用HandlerMapping#getHandler,而在getHandler中会调用该方法来获得HandlerExecutionChain,HandlerExecutionChain是由一系列的拦截器+handler组成的.代码如下:

      protected HandlerExecutionChain getHandlerExecutionChain(Object handler,
              HttpServletRequest request) {
          // 1. 调用父类的处理
          HandlerExecutionChain chain = super.getHandlerExecutionChain(handler, request);
          // 2. 如果securityInterceptor 等于null或者是CORS请求,则直接返回
          if (this.securityInterceptor == null || CorsUtils.isCorsRequest(request)) {
              return chain;
          }
          // 3. 在原先的Interceptor基础上加上securityInterceptor
          return addSecurityInterceptor(chain);
      }
      1. 调用父类的处理
      2. 如果securityInterceptor 等于null或者是CORS请求,则直接返回
      3. 在原先的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()]));
        }

        关于securityInterceptor我们后面有讲解

    5. extendInterceptors –> 该方法是在AbstractHandlerMapping#initApplicationContext中调用的,调用的时间是在该HandlerMapping初始化时调用的.代码如下:

      protected void extendInterceptors(List<Object> interceptors) {
          interceptors.add(new SkipPathExtensionContentNegotiation());
      }

      SkipPathExtensionContentNegotiation,在前置处理中向request中保存了org.springframework.web.accept.PathExtensionContentNegotiationStrategy.SKIP的属性,值为true.代码如下:

      public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
              Object handler) throws Exception {
          request.setAttribute(SKIP_ATTRIBUTE, Boolean.TRUE);
          return true;
      }
    6. initCorsConfiguration–>复写了RequestMappingHandlerMapping#initCorsConfiguration,直接使用本类配置的corsConfiguration,该方法在注册Handler时有用.代码如下:

      protected CorsConfiguration initCorsConfiguration(Object handler, Method method, RequestMappingInfo mappingInfo) { return this.corsConfiguration; }

MvcEndpointSecurityInterceptor

AbstractEndpointHandlerMapping中持有的securityInterceptor默认是MvcEndpointSecurityInterceptor(自动装配).

  1. 其字段,构造器如下:

    private static final Log logger = LogFactory
            .getLog(MvcEndpointSecurityInterceptor.class);
    
    // 是否进行校验
    private final boolean secure;
    
    // 默认为ACTUATOR
    private final List<String> roles;
    
    private AtomicBoolean loggedUnauthorizedAttempt = new AtomicBoolean();
    
    public MvcEndpointSecurityInterceptor(boolean secure, List<String> roles) {
        this.secure = secure;
        this.roles = roles;
    }
    
  2. preHandle方法如下:

    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;
    }
    1. 如果是(cors请求并且是options类型的请求并且请求头Access-Control-Request-Method存在)或者(不进行校验),则直接返回true
    2. 如果是options请求并且HandlerMethod不是MvcEndpoint的实例,则返回true
    3. 如果配置的endpoints.sensitive=false或者对应的endpoints.xxx.sensitive=false,则直接返回true
    4. 如果拥有相应的权限则返回true.代码如下:

      private boolean isUserAllowedAccess(HttpServletRequest request) {
          AuthoritiesValidator authoritiesValidator = null;
          // 1. 如果org.springframework.security.config.annotation.web.WebSecurityConfigurer在当前类路径下存在,也就是加入了spring-boot-starter-security的依赖
          // 则实例化AuthoritiesValidator
          if (isSpringSecurityAvailable()) {
              authoritiesValidator = new AuthoritiesValidator();
          }
          // 2. 遍历需要的权限,依次判断其是否有对应的权限,只要有1个满足的话,则返回true
          for (String role : this.roles) {
              // 2.1 判断用户是否是某个种类的角色,类型可以在web.xml中配置(tomcat-users.xml)
              if (request.isUserInRole(role)) {
                  return true;
              }
              // 2.2 如果authoritiesValidator不等于null并且拥有对应的权限,则返回true
              if (authoritiesValidator != null && authoritiesValidator.hasAuthority(role)) {
                  return true;
              }
          }
          // 3. 如果都不满足,返回false
          return false;
      }
      1. 如果org.springframework.security.config.annotation.web.WebSecurityConfigurer在当前类路径下存在,也就是加入了spring-boot-starter-security的依赖,则实例化AuthoritiesValidator
      2. 遍历需要的权限,依次判断其是否有对应的权限,只要有1个满足的话,则返回true

        1. 判断用户是否是某个种类的角色,类型可以在web.xml中配置(tomcat-users.xml)
        2. 如果authoritiesValidator不等于null并且拥有对应的权限,则返回true.代码如下:

          private boolean hasAuthority(String role) {
              Authentication authentication = SecurityContextHolder.getContext()
                      .getAuthentication();
              if (authentication != null) {
                  for (GrantedAuthority authority : authentication.getAuthorities()) {
                      if (authority.getAuthority().equals(role)) {
                          return true;
                      }
                  }
              }
              return false;
          }
      3. 如果都不满足,返回false

    5. 返回401

EndpointHandlerMapping

EndpointHandlerMapping 继承自AbstractEndpointHandlerMapping.代码如下:

public class EndpointHandlerMapping extends AbstractEndpointHandlerMapping<MvcEndpoint> {


    public EndpointHandlerMapping(Collection<? extends MvcEndpoint> endpoints) {
        super(endpoints);
    }

    // 创建一个EndpointHandlerMapping.
    public EndpointHandlerMapping(Collection<? extends MvcEndpoint> endpoints,
            CorsConfiguration corsConfiguration) {
        super(endpoints, corsConfiguration);
    }

}

自动装配

EndpointHandlerMapping的自动装配是在EndpointWebMvcAutoConfiguration中.

  1. EndpointWebMvcAutoConfiguration声明了如下注解,在满足如下条件时生效:

    @Configuration
    @ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
    @ConditionalOnWebApplication
    @AutoConfigureAfter({ PropertyPlaceholderAutoConfiguration.class,
        EmbeddedServletContainerAutoConfiguration.class, WebMvcAutoConfiguration.class,
        ManagementServerPropertiesAutoConfiguration.class,
        RepositoryRestMvcAutoConfiguration.class, HypermediaAutoConfiguration.class,
        HttpMessageConvertersAutoConfiguration.class })
    • @ConditionalOnClass({ Servlet.class, DispatcherServlet.class }) –> 在类路径下存在Servlet.class, DispatcherServlet.class时生效
    • @ConditionalOnWebApplication –> 是web环境时生效
  2. EndpointHandlerMapping实现了ApplicationContextAware, BeanFactoryAware, SmartInitializingSingleton接口,其中,当EndpointHandlerMapping初始化之后会回调afterSingletonsInstantiated.代码如下:

    public void afterSingletonsInstantiated() {
        // 1. 判断 managementPort 是否和serverPort一样
        ManagementServerPort managementPort = ManagementServerPort.DIFFERENT;
        if (this.applicationContext instanceof WebApplicationContext) {
            managementPort = ManagementServerPort
                    .get(this.applicationContext.getEnvironment(), this.beanFactory);
        }
        // 2. 如果不一样
        if (managementPort == ManagementServerPort.DIFFERENT) {
            // 2.1 如果当前applicationContext为EmbeddedWebApplicationContext,并且有嵌入容器,则创建一个子WebApplicationContext
            // note: 这也意味着使用actuate时,如果managementPort 和serverPort 不一样时,并且是部署在外置tomcat中时,actuate 会不生效的 
            if (this.applicationContext instanceof EmbeddedWebApplicationContext
                    && ((EmbeddedWebApplicationContext) this.applicationContext)
                            .getEmbeddedServletContainer() != null) {
                createChildManagementContext();
            }
            else {
                // 否则,不进行创建
                logger.warn("Could not start embedded management container on "
                        + "different port (management endpoints are still available "
                        + "through JMX)");
            }
        }
        // 3. 如果一样,则
        if (managementPort == ManagementServerPort.SAME) {
            // 3.1 如果management.ssl.enabled 配置为true,则抛出IllegalStateException
            if (new RelaxedPropertyResolver(this.applicationContext.getEnvironment(),
                    "management.ssl.").getProperty("enabled", Boolean.class, false)) {
                throw new IllegalStateException(
                        "Management-specific SSL cannot be configured as the management "
                                + "server is not listening on a separate port");
            }
            // 3.2 向environment中添加名为Management Server的PropertySource,其中获得local.management.port时,会转而向environment调用local.server.port
            // 访问其他的属性直接返回null
            if (this.applicationContext
                    .getEnvironment() instanceof ConfigurableEnvironment) {
                addLocalManagementPortPropertyAlias(
                        (ConfigurableEnvironment) this.applicationContext
                                .getEnvironment());
            }
        }
    }
    1. 判断 managementPort 是否和serverPort一样.代码如下:

      public static ManagementServerPort get(Environment environment,
              BeanFactory beanFactory) {
          // 1. 获得server.port的配置
          Integer serverPort = getPortProperty(environment, "server.");
          // 2. 如果server.port没有配置并且ServerProperties在beanFactory中存在的话
          if (serverPort == null && hasCustomBeanDefinition(beanFactory,
                  ServerProperties.class, ServerPropertiesAutoConfiguration.class)) {
              // 则获得ServerProperties中所配置的默认端口
              serverPort = getTemporaryBean(beanFactory, ServerProperties.class)
                      .getPort();
          }
          // 3. 获得management.port的配置,如果management.port没有配置并且ManagementServerProperties在beanFactory中存在的话
          // 则获得ServerProperties中所配置的默认端口
          Integer managementPort = getPortProperty(environment, "management.");
          if (managementPort == null && hasCustomBeanDefinition(beanFactory,
                  ManagementServerProperties.class,
                  ManagementServerPropertiesAutoConfiguration.class)) {
              managementPort = getTemporaryBean(beanFactory,
                      ManagementServerProperties.class).getPort();
          }
          // 4. 如果managementPort端口号小于零则返回不可用
          if (managementPort != null && managementPort < 0) {
              return DISABLE;
          }
          /* * 5. 如果 * <ul> * <li>managementPort等于null(management.port没有配置)</li> * <li>managementPort 等于8080</li> * <li>managementPort 等于serverPort</li> * </ul> * 满足其中一个,则 managementPort 使用和serverPort一样的端口,返回same,否则,返回DIFFERENT * */
          return ((managementPort == null)
                  || (serverPort == null && managementPort.equals(8080))
                  || (managementPort != 0 && managementPort.equals(serverPort)) ? SAME
                          : DIFFERENT);
      }
      1. 获得server.port的配置
      2. 如果server.port没有配置并且ServerProperties在beanFactory中存在的话,则获得ServerProperties中所配置的默认端口
      3. 获得management.port的配置,如果management.port没有配置并且ManagementServerProperties在beanFactory中存在的话,则获得ServerProperties中所配置的默认端口
      4. 如果managementPort端口号小于零则返回不可用
      5. 如果满足以下条件的任意1个,则managementPort 使用和serverPort一样的端口,返回same,否则,返回DIFFERENT:

        1. managementPort等于null(management.port没有配置)
        2. managementPort 等于8080
        3. managementPort 等于serverPort
    2. 如果不一样

      1. 如果当前applicationContext为EmbeddedWebApplicationContext,并且有嵌入容器,则创建一个子WebApplicationContext

        这也意味着使用actuate时,如果managementPort 和serverPort 不一样时,并且是部署在外置tomcat中时,actuate 会不生效的

    3. 如果一样,则

      1. 如果management.ssl.enabled 配置为true,则抛出IllegalStateException
      2. 向environment中添加名为Management Server的PropertySource,其中获得local.management.port时,会转而向environment调用local.server.port.访问其他的属性直接返回null.代码如下:

        private void addLocalManagementPortPropertyAlias(
                final ConfigurableEnvironment environment) {
            environment.getPropertySources()
                    .addLast(new PropertySource<Object>("Management Server") {
                        @Override
                        public Object getProperty(String name) {
                            if ("local.management.port".equals(name)) {
                                return environment.getProperty("local.server.port");
                            }
                            return null;
                        }
                    });
        }

    其中:当 managementPort 和serverPort 不一样,会执行如下代码:

    private void createChildManagementContext() {
        AnnotationConfigEmbeddedWebApplicationContext childContext = new AnnotationConfigEmbeddedWebApplicationContext();
        childContext.setParent(this.applicationContext);
        childContext.setNamespace("management");
        childContext.setId(this.applicationContext.getId() + ":management");
        childContext.setClassLoader(this.applicationContext.getClassLoader());
        // 1. 注册配置类
        childContext.register(EndpointWebMvcChildContextConfiguration.class,
                PropertyPlaceholderAutoConfiguration.class,
                EmbeddedServletContainerAutoConfiguration.class,
                DispatcherServletAutoConfiguration.class);
        // 2. 注册嵌入容器Factory 
        registerEmbeddedServletContainerFactory(childContext);
        // 3. 添加CloseManagementContextListener监听器--> 处理ContextClosedEvent,ApplicationFailedEvent 事件
        CloseManagementContextListener.addIfPossible(this.applicationContext,
                childContext);
        // 4. 进行初始化
        childContext.refresh();
        // 5. 为ManagementContextResolver 重新设置ApplicationContext 为创建的子WebApplicationContext
        managementContextResolver().setApplicationContext(childContext);
    }
    1. 实例化AnnotationConfigEmbeddedWebApplicationContext
    2. 注册配置类
    3. 注册嵌入容器Factory.代码如下:

      private void registerEmbeddedServletContainerFactory(
              AnnotationConfigEmbeddedWebApplicationContext childContext) {
          try {
              ConfigurableListableBeanFactory beanFactory = childContext.getBeanFactory();
              if (beanFactory instanceof BeanDefinitionRegistry) {
                  BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
                  registry.registerBeanDefinition("embeddedServletContainerFactory",
                          new RootBeanDefinition(
                                  determineEmbeddedServletContainerFactoryClass()));
              }
          }
          catch (NoSuchBeanDefinitionException ex) {
              // Ignore and assume auto-configuration
          }
      }

      调用:

      private Class<?> determineEmbeddedServletContainerFactoryClass()
              throws NoSuchBeanDefinitionException {
          Class<?> servletContainerFactoryClass = this.applicationContext
                  .getBean(EmbeddedServletContainerFactory.class).getClass();
          if (cannotBeInstantiated(servletContainerFactoryClass)) {
              throw new FatalBeanException("EmbeddedServletContainerFactory implementation "
                      + servletContainerFactoryClass.getName() + " cannot be instantiated. "
                      + "To allow a separate management port to be used, a top-level class "
                      + "or static inner class should be used instead");
          }
          return servletContainerFactoryClass;
      }
    4. 添加CloseManagementContextListener监听器–> 处理ContextClosedEvent,ApplicationFailedEvent 事件.其最终处理都是关闭当前上下文.如下:

      private void propagateCloseIfNecessary(ApplicationContext applicationContext) {
          if (applicationContext == this.parentContext) {
              this.childContext.close();
          }
      }
    5. 进行初始化
    6. 为ManagementContextResolver 重新设置ApplicationContext 为创建的子WebApplicationContext
  3. EndpointWebMvcAutoConfiguration中有2个配置内部类:

    1. ApplicationContextFilterConfiguration,代码如下:

      @Configuration
      @ConditionalOnProperty(prefix = "management", name = "add-application-context-header", matchIfMissing = true, havingValue = "true")
      protected static class ApplicationContextFilterConfiguration {
      
          // ApplicationContextHeaderFilter是添加名为X-Application-Context的响应头,值为ApplicationContext的id
          @Bean
          public ApplicationContextHeaderFilter applicationContextIdFilter(
                  ApplicationContext context) {
              return new ApplicationContextHeaderFilter(context);
          }
      }

      当配置有management.add-application-context-header = true或者没有配置时默认生效.注册1个ApplicationContextHeaderFilter.其作用是添加名为X-Application-Context的响应头,值为ApplicationContext的id.代码如下:

      public static final String HEADER_NAME = "X-Application-Context";
      private final ApplicationContext applicationContext;
      public ApplicationContextHeaderFilter(ApplicationContext context) {
          this.applicationContext = context;
      }
      @Override
      protected void doFilterInternal(HttpServletRequest request,
              HttpServletResponse response, FilterChain filterChain)
                      throws ServletException, IOException {
          response.addHeader(HEADER_NAME, this.applicationContext.getId());
          filterChain.doFilter(request, response);
      }
    2. EndpointWebMvcConfiguration,代码如下:

      @Configuration
      @Conditional(OnManagementMvcCondition.class)
      @Import(ManagementContextConfigurationsImportSelector.class)
      protected static class EndpointWebMvcConfiguration {
      
      }

      当满足如下条件的任意1个时生效:

      1. managementPort等于null(management.port没有配置)
      2. managementPort 等于8080
      3. managementPort 等于serverPort

      通过@Import导入了ManagementContextConfigurationsImportSelector.由于其是DeferredImportSelector的实例,因此会调用其selectImports方法,如下:

      public String[] selectImports(AnnotationMetadata metadata) {
          // Find all management context configuration classes, filtering duplicates
          // 1. 加载/META-INF/spring.factories中配置的org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration
          List<ManagementConfiguration> configurations = getConfigurations();
          // 2. 排序
          OrderComparator.sort(configurations);
          List<String> names = new ArrayList<String>();
          for (ManagementConfiguration configuration : configurations) {
              names.add(configuration.getClassName());
          }
          return names.toArray(new String[names.size()]);
      }
      1. 加载/META-INF/spring.factories中配置的org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration
      2. 排序

      默认返回的是:

      org.springframework.boot.actuate.autoconfigure.EndpointWebMvcManagementContextConfiguration,\
      org.springframework.boot.actuate.autoconfigure.EndpointWebMvcHypermediaManagementContextConfiguration
      1. EndpointWebMvcHypermediaManagementContextConfiguration,其注解如下:

        @ManagementContextConfiguration
        @ConditionalOnClass(Link.class)
        @ConditionalOnWebApplication
        @ConditionalOnBean(HttpMessageConverters.class)
        @Conditional(EndpointHypermediaEnabledCondition.class)
        @EnableConfigurationProperties(ResourceProperties.class)

        由于默认情况下,在类路径下不存在org.springframework.hateoas.Link.class,因此该类不会进行处理.

      2. EndpointWebMvcManagementContextConfiguration,在这个配置类中,除了配置了一系列的mvcEndpoints之外,还配置了endpointHandlerMapping.如下:

        @Bean
        @ConditionalOnMissingBean
        public EndpointHandlerMapping endpointHandlerMapping() {
            // 1. 获得注册的MvcEndpoint
            Set<MvcEndpoint> endpoints = mvcEndpoints().getEndpoints();
            // 2. 实例化CorsConfiguration
            CorsConfiguration corsConfiguration = getCorsConfiguration(this.corsProperties);
            // 3. 实例化EndpointHandlerMapping-->actuate 系列的HandlerMapping
            EndpointHandlerMapping mapping = new EndpointHandlerMapping(endpoints,
                    corsConfiguration);
            // 4. 设置EndpointHandlerMapping的前缀为management.contextPath 的配置值
            mapping.setPrefix(this.managementServerProperties.getContextPath());
            // 5. 实例化MvcEndpointSecurityInterceptor--> Interceptor,并进行设置
            MvcEndpointSecurityInterceptor securityInterceptor = new MvcEndpointSecurityInterceptor(
                    this.managementServerProperties.getSecurity().isEnabled(),
                    this.managementServerProperties.getSecurity().getRoles());
            mapping.setSecurityInterceptor(securityInterceptor);
            // 6. 个性化设置--> 默认没有实现类
            for (EndpointHandlerMappingCustomizer customizer : this.mappingCustomizers) {
                customizer.customize(mapping);
            }
            return mapping;
        }
        1. 获得注册的MvcEndpoint
        2. 实例化CorsConfiguration
        3. 实例化EndpointHandlerMapping–>actuate 系列的HandlerMapping
        4. 设置EndpointHandlerMapping的前缀为management.contextPath 的配置值
        5. 实例化MvcEndpointSecurityInterceptor–> Interceptor,并进行设置
        6. 个性化设置–> 默认没有实现类

    此刻就将EndpointHandlerMapping注册了,当DispatcherServlet第1次实例化或者当BeanFactory发送ContextRefreshedEvent事件时,就会调用DispatcherServlet的initHandlerMappings方法,在该方法中,将BeanFactory中所有的HandlerMapping的bean都加入到了handlerMappings中.代码如下:

    private void initHandlerMappings(ApplicationContext context) {
        this.handlerMappings = null;
    
        if (this.detectAllHandlerMappings) {
            // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
            Map<String, HandlerMapping> matchingBeans =
                    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
            if (!matchingBeans.isEmpty()) {
                this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
                // We keep HandlerMappings in sorted order.
                AnnotationAwareOrderComparator.sort(this.handlerMappings);
            }
        }
        else {
            try {
                HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
                this.handlerMappings = Collections.singletonList(hm);
            }
            catch (NoSuchBeanDefinitionException ex) {
                // Ignore, we'll add a default HandlerMapping later.
            }
        }
    
        // Ensure we have at least one HandlerMapping, by registering
        // a default HandlerMapping if no other mappings are found.
        if (this.handlerMappings == null) {
            this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
            if (logger.isDebugEnabled()) {
                logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
            }
        }
    }

    调用链如下:

    《spring boot 源码解析55-spring boot actuator HandlerMapping全网独家揭秘》

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