spring boot 源码解析52-actuate中MVCEndPoint解析

article/2025/11/9 12:52:33

前言

之前的几篇文章分析了spring boot 中有关endpoint的实现,细心的朋友可以发现,在org.springframework.boot.actuate.endpoint.mvc 包下也有一系列的xxxEndpoint,这又是为什么呢?

原因是: 我们很多情况下,都是访问接口的方式获取应用的监控,之前的分析是其实现的底层,要想实现通过接口访问,还需要对其进行包装一番,org.springframework.boot.actuate.endpoint.mvc 包下的实现就是干的这种事,下面,我们就来分析一下吧

解析

关于mvcEndPoint的类图如下,

mvc-endpoint-class-uml

下面我们就来1个1个的来分析吧.

MvcEndpoint

  1. MvcEndpoint –> 顶层接口,实现类允许使用@RequestMapping和完整的Spring MVC机制,但不能在类型级别使用@Controller或@RequestMapping,因为这将导致路径的双重映射,一次通过常规MVC处理程序映射,一次通过EndpointHandlerMapping。

  2. 该类声明如下:

    public interface MvcEndpoint {// 禁用端点的响应实体
    ResponseEntity<Map<String, String>> DISABLED_RESPONSE = new ResponseEntity<Map<String, String>>(Collections.singletonMap("message", "This endpoint is disabled"),HttpStatus.NOT_FOUND);// 返回端点的MVC路径
    String getPath();// 返回端点是否暴露敏感信息
    boolean isSensitive();// 返回端点暴露的类型。或者返回null 如果当前的MvcEndpoint 暴露的信息 不能视为一个传统的Endpoint(也就是我们之前分析的那一堆Endpoint)
    Class<? extends Endpoint> getEndpointType();
    }

AbstractMvcEndpoint

  1. AbstractMvcEndpoint继承了WebMvcConfigurerAdapter,实现了MvcEndpoint, EnvironmentAware接口,此时 AbstractMvcEndpoint 就持有了Environment,可以对spring mvc 做个性化设置.

  2. 字段,构造器分别如下:

    // Endpoint 请求路径
    private String path;// endpoint是否可用
    private Boolean enabled;// 标识该endpoint 是否暴露了敏感数据,如果为true,访问该Endpoint需要进行校验
    private Boolean sensitive;// 是否默认敏感
    private final boolean sensitiveDefault;public AbstractMvcEndpoint(String path, boolean sensitive) {setPath(path);this.sensitiveDefault = sensitive;
    }public AbstractMvcEndpoint(String path, boolean sensitive, boolean enabled) {setPath(path);this.sensitiveDefault = sensitive;this.enabled = enabled;
    }
  3. AbstractMvcEndpoint 实现了MvcEndpoint的方法,分别如下:

    1. getPath,直接返回其字段值即可,代码如下:

      public String getPath() {
      return this.path;
      }
    2. isSensitive–> 默认返回false.代码如下:

      public boolean isSensitive() {
      // 默认返回false
      return EndpointProperties.isSensitive(this.environment, this.sensitive,this.sensitiveDefault);
      }

      调用:

      public static boolean isSensitive(Environment environment, Boolean sensitive,boolean sensitiveDefault) {
      // 1. 如果sensitive 不等于null,则直接返回
      if (sensitive != null) {return sensitive;
      }
      // 2. 如果environment 不等于null 并且 environment中配置有endpoints.sensitive的属性,则
      // 返回其配置值
      if (environment != null&& environment.containsProperty(ENDPOINTS_SENSITIVE_PROPERTY)) {return environment.getProperty(ENDPOINTS_SENSITIVE_PROPERTY, Boolean.class);
      }
      // 3. 返回给的默认值
      return sensitiveDefault;
      }
      1. 如果sensitive 不等于null,则直接返回
      2. 如果environment 不等于null 并且 environment中配置有endpoints.sensitive的属性,则 返回其配置值
      3. 返回给的默认值
    3. getEndpointType –> 默认返回null,代码如下:

      public Class<? extends Endpoint> getEndpointType() {
      return null;
      }
  4. 此外,该类还声明了1个方法,判断当前端点是否可用,默认返回true代码如下:

    public boolean isEnabled() {// 默认返回truereturn EndpointProperties.isEnabled(this.environment, this.enabled);
    }

    调用:

    public static boolean isEnabled(Environment environment, Boolean enabled) {// 1. 如果enabled 不为null,则进行返回.if (enabled != null) {return enabled;}// 2. 如果Environment 不等于null 并且Environment 配置有endpoints.enabled的属性,// 则返回其配置的值if (environment != null&& environment.containsProperty(ENDPOINTS_ENABLED_PROPERTY)) {return environment.getProperty(ENDPOINTS_ENABLED_PROPERTY, Boolean.class);}// 3. 默认为truereturn true;
    }
    1. 如果enabled 不为null,则进行返回.
    2. 如果Environment 不等于null 并且Environment 配置有endpoints.enabled的属性,则返回其配置的值
    3. 默认为true

NamedMvcEndpoint

  1. NamedMvcEndpoint继承自MvcEndpoint,使一个MvcEndpoint可以包含一个逻辑名.不像getPath()–>它没有给用户一个机会去改变endpoint的名字.NamedMvcEndpoint 提供了一个一致的方式去引用一个endpoint.

  2. 该类声明了1个方法,如下:

    // 返回一个逻辑名字,不能为null,空,字母数字组成
    String getName();

AbstractNamedMvcEndpoint

  1. 该类继承自AbstractMvcEndpoint,实现了NamedMvcEndpoint接口.
  2. 字段,构造器如下:

    // Endpoint 名字
    private final String name;public AbstractNamedMvcEndpoint(String name, String path, boolean sensitive) {super(path, sensitive);Assert.hasLength(name, "Name must not be empty");this.name = name;
    }public AbstractNamedMvcEndpoint(String name, String path, boolean sensitive,boolean enabled) {super(path, sensitive, enabled);Assert.hasLength(name, "Name must not be empty");this.name = name;
    }
  3. getName,只需返回name属性值即可,很简单,代码如下:

    public String getName() {return this.name;
    }

该类的子类有:

  • AuditEventsMvcEndpoint
  • DocsMvcEndpoint
  • HalJsonMvcEndpoint
  • HeapdumpMvcEndpoint
  • JolokiaMvcEndpoint
  • LogFileMvcEndpoint

这些子类的实现我们后续进行分析…

AbstractEndpointMvcAdapter

  1. AbstractEndpointMvcAdapter 实现了NamedMvcEndpoint接口,是MvcEndpoint的抽象基类
  2. 字段,构造器如下:

    // 被代理的底层端点(端点子类
    private final E delegate;// 端点URL路径
    private String path;// 
    public AbstractEndpointMvcAdapter(E delegate) {Assert.notNull(delegate, "Delegate must not be null");this.delegate = delegate;
    }
  3. NamedMvcEndpoint接口方法实现如下:

    1. getName,返回被代理的Endpoint的id,代码如下:

      public String getName() {
      return this.delegate.getId();
      }
    2. getPath –> 如果path不等于null,则直接返回,否则使用/+Endpoint的id,代码如下:

      public String getPath() {
      // 如果path不等于null,则直接返回,否则使用/+Endpoint的id
      return (this.path != null ? this.path : "/" + this.delegate.getId());
      }
    3. isSensitive–> 直接调用被代理的Endpoint的isSensitive方法即可.代码如下:

      public boolean isSensitive() {
      return this.delegate.isSensitive();
      }
    4. getEndpointType –> 返回被代理的 Endpoint的类型即可,代码如下:

      public Class<? extends Endpoint> getEndpointType() {
      return this.delegate.getClass();
      }
  4. AbstractEndpointMvcAdapter 还声明了2个方法,供子类使用

    1. getDisabledResponse–> 返回该相应当所代理的endpoint不可用时.代码如下:

      protected ResponseEntity<?> getDisabledResponse() {
      return MvcEndpoint.DISABLED_RESPONSE;
      }

      即:

      ResponseEntity<Map<String, String>> DISABLED_RESPONSE = new ResponseEntity<Map<String, String>>(Collections.singletonMap("message", "This endpoint is disabled"),HttpStatus.NOT_FOUND);
    2. invoke–> 调用代理的Endpoint,并返回调用结果,代码如下:

      protected Object invoke() {
      if (!this.delegate.isEnabled()) {return getDisabledResponse();
      }
      return this.delegate.invoke();
      }
      1. 端点不可用(禁用),则返回默认的不可用信息.当Endpoint被禁用时,是不会注册的.
      2. 否则,调用Endpoint的invoke方法

EndpointMvcAdapter

  1. EndpointMvcAdapter,继承自AbstractEndpointMvcAdapter,将Endpoint适配为MvcEndpoint. 构造器如下:

    public EndpointMvcAdapter(Endpoint<?> delegate) {super(delegate);
    }
  2. 该类覆写了invoke,使其能过被spring mvc 处理–> 暴露接口(关于这部分,我们后续有分析),代码如下:

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

    其中@ActuatorGetMapping 就是@RequestMapping的封装,被该注解标注的方法,其请求方式为get,产生的数据格式为application/vnd.spring-boot.actuator.v1+json和application/json.代码如下:

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @RequestMapping(method = RequestMethod.GET, produces = {ActuatorMediaTypes.APPLICATION_ACTUATOR_V1_JSON_VALUE,MediaType.APPLICATION_JSON_VALUE })
    @interface ActuatorGetMapping {/*** Alias for {@link RequestMapping#value}.* @return the value*/
    @AliasFor(annotation = RequestMapping.class)
    String[] value() default {};
    }

EnvironmentMvcEndpoint

  1. 该类继承自EndpointMvcAdapter,实现了EnvironmentAware,因此,该类也就持有了Environment.

  2. 字段,构造器如下:

    private Environment environment;public EnvironmentMvcEndpoint(EnvironmentEndpoint delegate) {super(delegate);
    }

    此时, EnvironmentMvcEndpoint 也就持有了EnvironmentEndpoint的实例

  3. 该类声明了1个被@ActuatorGetMapping注解的方法,value,代码如下:

    @ActuatorGetMapping("/{name:.*}")
    @ResponseBody
    @HypermediaDisabled
    public Object value(@PathVariable String name) {if (!getDelegate().isEnabled()) {// Shouldn't happen - MVC endpoint shouldn't be registered when delegate's// disabledreturn getDisabledResponse();}return new NamePatternEnvironmentFilter(this.environment).getResults(name);
    }
    • @ActuatorGetMapping(“/{name:.*}”)与@PathVariable String name –> rest风格,将其name注入到方法的参数name中,匹配规则是任意字符
    • @ResponseBody –> 返回json格式的数据
    • @HypermediaDisabled–>表明该MvcEndpoint或者@RequestMapping注解的方法不会生成hypermedia 的响应.代码如下:

      @Target({ ElementType.TYPE, ElementType.METHOD })
      @Retention(RetentionPolicy.RUNTIME)
      @Documented
      public @interface HypermediaDisabled {
      }

    其方法逻辑如下:

    1. 如果EnvironmentEndpoint不可用,则返回Disabled Response.
    2. 否则实例化NamePatternEnvironmentFilter,调用其getResults方法获得对应name的属性值.

      NamePatternEnvironmentFilter继承自NamePatternFilter.

      NamePatternFilter:

      1. NamePatternFilter–> 可以使用name正则表达式过滤源数据的实用工具类,用来检测检测名称是否是经典的“single value”键或正则表达式.子类必须实现getValue,getNames 方法.该类是1个泛型类,其泛型参数T 代表着原始数据的类型.

      2. 字段,构造器如下:

        private static final String[] REGEX_PARTS = { "*", "$", "^", "+", "[" };
        private final T source;
        NamePatternFilter(T source) {
        this.source = source;
        }
      3. 声明了3个抽象方法:

        protected abstract void getNames(T source, NameCallback callback);
        protected abstract Object getValue(T source, String name);
        protected abstract Object getOptionalValue(T source, String name);
      4. NamePatternFilter中声明了1个方法,代码如下:

        public Map<String, Object> getResults(String name) {
        // 1. 如果name含有 "*", "$", "^", "+", "[" ,则认为是一个正则表达式,将其返回Pattern.否则返回null
        Pattern pattern = compilePatternIfNecessary(name);
        if (pattern == null) {
        // 2. 如果pattern 等于null,则说明name 是一个普通字符串,调用getValue 这一抽象方法获得value,放入result后返回
        Object value = getValue(this.source, name);
        Map<String, Object> result = new HashMap<String, Object>();
        result.put(name, value);
        return result;
        }
        // 3. 实例化 ResultCollectingNameCallback
        ResultCollectingNameCallback resultCollector = new ResultCollectingNameCallback(pattern);
        // 4. 抽象方法
        getNames(this.source, resultCollector);
        // 5, 返回结果
        return resultCollector.getResults();
        }
        1. 如果name含有 “*”, “$”, “^”, “+”, “[” ,则认为是一个正则表达式,将其返回Pattern.否则返回null,代码如下:

          private Pattern compilePatternIfNecessary(String name) {
          for (String part : REGEX_PARTS) {
          if (name.contains(part)) {
          try {return Pattern.compile(name);
          }
          catch (PatternSyntaxException ex) {return null;
          }
          }
          }
          return null;
          }
          
        2. 如果pattern 等于null,则说明name 是一个普通字符串,调用getValue 这一抽象方法获得value,放入result后返回
        3. 实例化 ResultCollectingNameCallback,该类实现了NameCallback接口,该接口只声明了如下方法:

          void addName(String name);

          ResultCollectingNameCallback中的字段,构造器如下:

          // 将name 转换为正则所对应的对象
          private final Pattern pattern;
          // 结果集
          private final Map<String, Object> results = new LinkedHashMap<String, Object>();
          ResultCollectingNameCallback(Pattern pattern) {
          this.pattern = pattern;
          }

          其addName方法如下:

          public void addName(String name) {
          // 1. 如果name 符合正则,则通过调用getOptionalValue 获得值后加入到results
          if (this.pattern.matcher(name).matches()) {
          Object value = getOptionalValue(NamePatternFilter.this.source, name);
          if (value != null) {this.results.put(name, value);
          }
          }
          }

          如果name 符合正则,则通过调用getOptionalValue 获得值后加入到results

          此外还声明了get方法,来暴露结果集,代码如下:

          public Map<String, Object> getResults() {
          return this.results;
          }
        4. 执行getNames方法,进行结果的收集

        5. 返回结果

      NamePatternEnvironmentFilter

      1. 继承了NamePatternFilter 接口,泛型参数为Environment
      2. 抽象方法分别实现如下:

        1. getValue:

          protected Object getValue(Environment source, String name) {
          // 1. 获取值,如果没获取到,则抛出NoSuchPropertyException,否则,对其进行脱敏
          Object result = getValue(name);
          if (result == null) {
          throw new NoSuchPropertyException("No such property: " + name);
          }
          return ((EnvironmentEndpoint) getDelegate()).sanitize(name, result);
          }

          调用getValue方法获取值,如果没获取到,则抛出NoSuchPropertyException,否则,对其进行脱敏.

          getValue–>直接从environment 中获取值,代码如下:

          private Object getValue(String name) {
          // 直接从environment 中获取值,
          return ((EnvironmentEndpoint) getDelegate()).getResolver().getProperty(name,Object.class);
          }
        2. getNames–> 遍历source中的PropertySources,将PropertySource的属性名依次的加入到ResultCollectingNameCallback中.代码如下:

          protected void getNames(Environment source, NameCallback callback) {
          if (source instanceof ConfigurableEnvironment) {
          // 遍历source中的PropertySources,将PropertySource的属性名依次的加入到ResultCollectingNameCallback中
          getNames(((ConfigurableEnvironment) source).getPropertySources(),callback);
          }
          }
          private void getNames(PropertySources propertySources, NameCallback callback) {
          for (PropertySource<?> propertySource : propertySources) {
          if (propertySource instanceof EnumerablePropertySource) {EnumerablePropertySource<?> source = (EnumerablePropertySource<?>) propertySource;for (String name : source.getPropertyNames()) {callback.addName(name);}
          }
          }
          }
        3. getOptionalValue,代码如下:

          protected Object getOptionalValue(Environment source, String name) {
          // 1. 获得name对应的属性值
          Object result = getValue(name);
          if (result != null) {
          // 2. 如果属性值存在则进行脱敏
          result = ((EnvironmentEndpoint) getDelegate()).sanitize(name, result);
          }
          // 3. 否则直接返回null
          return result;
          }
          1. 获得name对应的属性值
          2. 如果属性值存在则进行脱敏
          3. 否则直接返回null
  4. 属性配置(有@ConfigurationProperties(prefix = “endpoints.env”)注解):

    endpoints.env.path=/env
  5. 自动装配:

    在EndpointWebMvcManagementContextConfiguration中进行了配置,如下:

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnBean(EnvironmentEndpoint.class)
    @ConditionalOnEnabledEndpoint("env")
    public EnvironmentMvcEndpoint environmentMvcEndpoint(EnvironmentEndpoint delegate) {return new EnvironmentMvcEndpoint(delegate);
    }
    • @Bean –> 注册1个id为 environmentMvcEndpoint,类型为EnvironmentMvcEndpoint的bean
    • @ConditionalOnMissingBean–>BeanFactory中不存在EnvironmentMvcEndpoint类型的bean时生效
    • @ConditionalOnBean(EnvironmentEndpoint.class) –> 当BeanFactory中存在EnvironmentEndpoint类型的Bean时生效
    • @ConditionalOnEnabledEndpoint(“env”)–> 如果配置有endpoints.env.enabled = true 或者endpoints.enabled= true 则该配置生效.关于此处的实现我们后续有文章进行分析.

LoggersMvcEndpoint

  1. 字段,构造器如下:

    private final LoggersEndpoint delegate;public LoggersMvcEndpoint(LoggersEndpoint delegate) {super(delegate);this.delegate = delegate;
    }
  2. 该类声明了2个@ActuatorGetMapping(“/{name:.*}”)注解的方法:

    1. get,代码如下:

      @ActuatorGetMapping("/{name:.*}")
      @ResponseBody
      @HypermediaDisabled
      public Object get(@PathVariable String name) {
      if (!this.delegate.isEnabled()) {// Shouldn't happen - MVC endpoint shouldn't be registered when delegate's// disabledreturn getDisabledResponse();
      }
      LoggerLevels levels = this.delegate.invoke(name);
      return (levels == null ? ResponseEntity.notFound().build() : levels);
      }
      • @ActuatorGetMapping(“/{name:.*}”) 与 @PathVariable String name–> rest风格,将其name注入到方法的参数name中,匹配规则是任意字符

      方法逻辑如下:

      1. 如果LoggersEndpoint不可用,则返回默认的不可用消息
      2. 否则,调用LoggersEndpoint#invoke 获得LoggerLevels.代码如下:

        public LoggerLevels invoke(String name) {
        Assert.notNull(name, "Name must not be null");
        LoggerConfiguration configuration = this.loggingSystem.getLoggerConfiguration(name);
        return (configuration == null ? null : new LoggerLevels(configuration));
        }
        1. 调用LoggingSystem#getLoggerConfiguration 获得LoggerConfiguration
        2. 如果LoggerConfiguration等于null,则返回null,否则,返回LoggerLevels

        由于spring boot 默认使用的是logback,因此,此处调用的是LogbackLoggingSystem中的实现,代码如下:

        public LoggerConfiguration getLoggerConfiguration(String loggerName) {
        return getLoggerConfiguration(getLogger(loggerName));
        }
        1. 调用getLogger 获得对应的logger,代码如下:

          private ch.qos.logback.classic.Logger getLogger(String name) {
          LoggerContext factory = getLoggerContext();
          if (StringUtils.isEmpty(name) || ROOT_LOGGER_NAME.equals(name)) {
          name = Logger.ROOT_LOGGER_NAME;
          }
          return factory.getLogger(name);
          }
          1. 获得LoggerContext
          2. 如果name为空,或者name等于ROOT,则name赋值为ROOT
          3. 根据name获得对应的Logger
        2. 调用getLoggerConfiguration获得对应的LoggerConfiguration.

          private LoggerConfiguration getLoggerConfiguration(
          ch.qos.logback.classic.Logger logger) {
          if (logger == null) {
          return null;
          }
          LogLevel level = LEVELS.convertNativeToSystem(logger.getLevel());
          LogLevel effectiveLevel = LEVELS
          .convertNativeToSystem(logger.getEffectiveLevel());
          String name = logger.getName();
          if (!StringUtils.hasLength(name) || Logger.ROOT_LOGGER_NAME.equals(name)) {
          name = ROOT_LOGGER_NAME;
          }
          return new LoggerConfiguration(name, level, effectiveLevel);
          }
          1. 如果logger等于null,返回null
          2. 根据logger对应的level,影响的Level分别获得LogLevel
          3. 获得logger对应的name,如果name等于null,或者等于root,则将其赋值为root
          4. 实例化LoggerConfiguration进行返回
      3. 如果 LoggerLevels,则返回ResponseEntity–>状态码为404,否则,直接返回LoggerLevels
    2. set–>该方法用于设置logger的日志级别.代码如下:

      @ActuatorPostMapping("/{name:.*}")
      @ResponseBody
      @HypermediaDisabled
      public Object set(@PathVariable String name,@RequestBody Map<String, String> configuration) {
      // 1. 如果不可用,则返回默认的不可用信息
      if (!this.delegate.isEnabled()) {// Shouldn't happen - MVC endpoint shouldn't be registered when delegate's// disabledreturn getDisabledResponse();
      }
      // 2. 根据configuration获得LogLevel,然后对指定的logger设置日志级别
      LogLevel logLevel = getLogLevel(configuration);
      this.delegate.setLogLevel(name, logLevel);
      // 3. 返回ok
      return ResponseEntity.ok().build();
      }

      逻辑如下:

      1. 如果不可用,则返回默认的不可用信息
      2. 根据configuration获得LogLevel,代码如下:

        private LogLevel getLogLevel(Map<String, String> configuration) {
        String level = configuration.get("configuredLevel");
        try {
        return (level == null ? null : LogLevel.valueOf(level.toUpperCase()));
        }
        catch (IllegalArgumentException ex) {
        throw new InvalidLogLevelException(level);
        }
        }
        1. 获得configuredLevel对应的值
        2. 如果没有配置,则返回null,否则,返回LogLevel,合法值有TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF.如果值不合法,抛出InvalidLogLevelException异常.由于InvalidLogLevelException类注有@ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = “No such log level”),因此,当抛出该异常时,会返回相应的信息.
      3. 对指定的logger设置日志级别,代码如下:

        public void setLogLevel(String name, LogLevel level) {
        Assert.notNull(name, "Name must not be empty");
        this.loggingSystem.setLogLevel(name, level);
        }

        默认情况下,会执行LogbackLoggingSystem#setLogLevel,代码如下:

        public void setLogLevel(String loggerName, LogLevel level) {
        ch.qos.logback.classic.Logger logger = getLogger(loggerName);
        if (logger != null) {
        logger.setLevel(LEVELS.convertSystemToNative(level));
        }
        }
        1. 根据对应的logger名获得Logger
        2. 将传入的loggerLevel转换为LogBack对应的日志级别
        3. 修改日志级别
      4. 返回ok

      至此,我们明白,要想修改日志级别,可以通过对/loggers/logger名,发送post请求,传入类上如下格式的参数:

      {"configuredLevel":"INFO"}

      即可修改日志级别

  3. 参数配置–>因为该类有@ConfigurationProperties(prefix = “endpoints.loggers”)注解:

    endpoints.loggers.path=/logfile # Endpoint path
  4. 自动化装配:

    在EndpointWebMvcManagementContextConfiguration中进行了声明,代码如下:

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnBean(LoggersEndpoint.class)
    @ConditionalOnEnabledEndpoint("loggers")
    public LoggersMvcEndpoint loggersMvcEndpoint(LoggersEndpoint delegate) {return new LoggersMvcEndpoint(delegate);
    }
    • @Bean –> 注册1个id为loggersMvcEndpoint,类型为LoggersMvcEndpoint的bean
    • @ConditionalOnMissingBean–> 当BeanFactory中不包含类型为LoggersMvcEndpoint的bean时生效
    • @ConditionalOnBean(LoggersEndpoint.class)–> 当BeanFactory中存在LoggersEndpoint类型的bean时生效
    • @ConditionalOnEnabledEndpoint(“loggers”)–> 如果配置有endpoints. loggers.enabled = true 或者endpoints.enabled= true 则该配置生效

ShutdownMvcEndpoint

该类继承自EndpointMvcAdapter

  1. 构造器如下:

    public ShutdownMvcEndpoint(ShutdownEndpoint delegate) {super(delegate);
    }
  2. invoke 实现如下:

    @PostMapping(produces = { ActuatorMediaTypes.APPLICATION_ACTUATOR_V1_JSON_VALUE,MediaType.APPLICATION_JSON_VALUE })
    @ResponseBody
    @Override
    public Object invoke() {if (!getDelegate().isEnabled()) {return new ResponseEntity<Map<String, String>>(Collections.singletonMap("message", "This endpoint is disabled"),HttpStatus.NOT_FOUND);}return super.invoke();
    }

    如果ShutdownEndpoint不可用,则返回{message:This endpoint is disabled},否则,调用ShutdownEndpoint#invoke,关闭spring boot 程序

  3. 参数配置–> 因为该类声明了@ConfigurationProperties(prefix = “endpoints.shutdown”):

    endpoints.shutdown.path= # Endpoint path.
  4. 自动装配:

    在EndpointWebMvcManagementContextConfiguration中进行了声明,代码如下:

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnBean(ShutdownEndpoint.class)
    @ConditionalOnEnabledEndpoint(value = "shutdown", enabledByDefault = false)
    public ShutdownMvcEndpoint shutdownMvcEndpoint(ShutdownEndpoint delegate) {return new ShutdownMvcEndpoint(delegate);
    }
    • @Bean –> 注册1个id为shutdownMvcEndpoint,类型为ShutdownMvcEndpoint的bean
    • @ConditionalOnMissingBean–> 当BeanFactory中不包含类型为ShutdownMvcEndpoint的bean时生效
    • @ConditionalOnBean(ShutdownEndpoint.class)–> 当BeanFactory中存在ShutdownEndpoint类型的bean时生效
    • @ConditionalOnEnabledEndpoint(“shutdown”, enabledByDefault = false) –>如果配置有endpoints. shutdown.enabled = true则该配置生效,如果没有配置,该配置不生效

MetricsMvcEndpoint

MetricsMvcEndpoint–> 继承自EndpointMvcAdapter.

  1. 字段构造器如下:

    private final MetricsEndpoint delegate;public MetricsMvcEndpoint(MetricsEndpoint delegate) {super(delegate);this.delegate = delegate;
    }
  2. 该类声明了1个被@ActuatorGetMapping注解的方法,如下:

    @ActuatorGetMapping("/{name:.*}")
    @ResponseBody
    @HypermediaDisabled
    public Object value(@PathVariable String name) {if (!this.delegate.isEnabled()) {return getDisabledResponse();}return new NamePatternMapFilter(this.delegate.invoke()).getResults(name);
    }
    1. 如果不可用,则返回默认的不可用信息
    2. 实例化NamePatternMapFilter ,之后调用其getResults方法,根据传入的name 获得对应的map,key –>name,value–>name所对应的Metric的值

      NamePatternMapFilter:

      1. 继承了NamePatternFilter 接口,泛型参数为Map
      2. 抽象方法实现如下:

        1. getValue–> 调用getOptionalValue获得MetricsEndpoint中Metrics名字为传入的值所对应的Metrics的值.,如果获取不到,则抛出NoSuchMetricException.代码如下:

          protected Object getValue(Map<String, ?> source, String name) {Object value = getOptionalValue(source, name);if (value == null) {throw new NoSuchMetricException("No such metric: " + name);}return value;
          }

          NoSuchMetricException代码如下:

          @ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "No such metric")
          public static class NoSuchMetricException extends RuntimeException {public NoSuchMetricException(String string) {super(string);}}

          当抛出该异常时,返回的状态码为404,reason为No such metric

        2. getOptionalValue–> 获得MetricsEndpoint中Metrics名字为传入的值所对应的Metrics的值.代码如下:

          protected Object getOptionalValue(Map<String, ?> source, String name) {return source.get(name);
          }
        3. getNames–>遍历MetricsEndpoint中返回的Metrics名,如果Metrics名符合正则的话(MetricsMvcEndpoint#value方法传入的是正则),则加入到ResultCollectingNameCallback的result中.代码如下:

          protected void getNames(Map<String, ?> source, NameCallback callback) {for (String name : source.keySet()) {try {callback.addName(name);}catch (NoSuchMetricException ex) {// Metric with null value. Continue.}}
          }
  3. 自动装配:

    声明在EndpointWebMvcManagementContextConfiguration中.代码如下:

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnBean(MetricsEndpoint.class)
    @ConditionalOnEnabledEndpoint("metrics")
    public MetricsMvcEndpoint metricsMvcEndpoint(MetricsEndpoint delegate) {return new MetricsMvcEndpoint(delegate);
    }
    • @Bean –> 注册1个id为metricsMvcEndpoint,类型为MetricsMvcEndpoint的bean
    • @ConditionalOnMissingBean–> BeanFactory中不存在MetricsMvcEndpoint类型的bean时生效
    • @ConditionalOnBean(MetricsEndpoint.class)–> BeanFactory中存在MetricsEndpoint类型的bean时生效
    • @ConditionalOnEnabledEndpoint(“metrics”) –> 如果配置有endpoints. metrics.enabled = true 或者endpoints.enabled= true 则该配置生效

http://chatgpt.dhexx.cn/article/s9LaKyJP.shtml

相关文章

Unable to identify any set of controllers that can actuate the specified joints:

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 背景一、解决办法二、总结 背景 具体的报错如下&#xff1a; [ERROR] [1649324583.023988413]: Unable to identify any set of controllers that can actuate the…

springboot整合Actuator监控

springboot整合Actuator监控。 1.简要说明&#xff1a; Actuator提供了对springboot应用程序监视和管理的能力&#xff0c;可以选择通过使用HTTP Endpoint或者使用JMX来管理和监控springboot应用程序。 Actuator 允许通过Endpoints对springboot进行监控和交互。springboot内…

项目监控之Spring Boot 监控端点 Actuator 入门

1. 概述 应用在部署在生产环境下&#xff0c;我们还需要考虑应用的管理与监控。例如说&#xff0c;应用是否健康存活、应用的 JVM 监控信息、服务器的监控信息&#xff08;CPU、内存、磁盘等等&#xff09;。 如果我们为应用的管理与监控做相应的开发&#xff0c;是需要一定的…

spring boot 源码解析23-actuate使用及EndPoint解析

前言 spring boot 中有个很诱人的组件–actuator,可以对spring boot应用做监控,只需在pom文件中加入如下配置即可: <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></depen…

ESXi挂载NFS共享存储

通常VMware的整体架构由三个部分组成&#xff0c;虚拟化环境&#xff08;包括ESXi与vCenter以及VM&#xff09;&#xff0c;交换机&#xff08;通常为万兆交换机或光纤交换机&#xff09;&#xff0c;存储&#xff08;netap、EMC等&#xff09;。使用光纤交换机&#xff0c;ESX…

Docker容器中挂载NFS共享目录

之前在https://blog.csdn.net/fengbingchun/article/details/110561129 介绍过使用Dockerfile构建ubuntu 16.04镜像,并在容器中编译执行Messy_Test项目.这里介绍下如何在容器中挂载NFS服务器上的共享目录. Dockerfile内容如下&#xff1a; FROM ubuntu:16.04 LABEL maintaine…

LINUX 下创建NFS共享目录

Linux下创建NFS共享目录的步骤如下 实验中服务器端IP为10.201.86.204&#xff0c;客户端IP为10.201.86.2051.在服务器端格式化需要共享的磁盘 fdisk /dev/sdb mkfs.xfs -f /dev/sdb12.安装NFS软件包 Server端和客户端都要安装 rpm -qa |grep nfs-utils rpm -qa |grep rpcb…

NFS共享存储服务介绍与案例详细配置过程

目录 1&#xff0c;什么是NFS?2&#xff0c;NFS工作原理3&#xff0c;使用NFS发布共享资源4&#xff0c;NFS 挂载原理5&#xff0c;NFS服务所需软件及主要配置文件安装NFS服务&#xff0c;需要安装两个软件&#xff0c;分别是&#xff1a; 6&#xff0c; NFS的相关文件&#x…

NFS 共享目录

今天用迅为的itop-4412的开发板 挂载nfs 把流程写一下&#xff0c;和遇到的问题。图片用的开发手册的图片 我有的没截图 1、搭建 NFS 服务器 实现 NFS&#xff0c;需要一个主机作为 NFS 服务器&#xff0c;选择虚拟机 Ubuntu 作为主机。首先需要在 在虚拟机 Ubuntu 上安装 Ub…

Linux的NFS共享目录

准备工作 1.准备两台虚拟机&#xff0c;一台作为服务器&#xff0c;一台作为客户机&#xff1a; 配置服务器ip地址&#xff1a;192.168.101.2 配置客户机ip地址&#xff1a;192.168.101.3 2.关闭两台的防火墙&#xff1a;systemctl stop firewalld.service 检查防火墙是否…

NFS共享服务搭建详细流程

标题DNF共享服务搭建详细流程 1. 概述 NFS(Network File System) 网络文件系统&#xff0c;是FreeBSD支持的文件系统中的一种&#xff0c;它允许网络中的计算机之间通过TCP/IP网络共享资源。在NFS的应用中&#xff0c;本地NFS的客户端应用可以透明地读写位于远端NFS服务器上的…

(转)企业级NFS网络文件共享服务

企业级NFS网络文件共享服务 原文&#xff1a;http://www.cnblogs.com/chensiqiqi/archive/2017/03/10/6530859.html --本教学笔记是本人学习和工作生涯中的摘记整理而成&#xff0c;此为初稿&#xff08;尚有诸多不完善之处&#xff09;&#xff0c;为原创作品&#xff0c;允许…

YUM仓库及NFS共享服务理论

文章目录 一、YUM仓库1.YUM概述2.准备安装源3.访问YUM仓库 二、FNS共享存储服务1.NFS概念2.NFS使用场景3.NFS服务 一、YUM仓库 1.YUM概述 YUM(Yellow dog Updater Modified) ●基于RPM包构建的软件更新机制 ●可以自动解决依赖关系 ●所有软件包由集中的YUM软件仓库提供 2.准…

NFS共享

nfs 简介 nfs特点 NFS&#xff08;Network File System&#xff09;网络文件系统&#xff0c;是FreeBSD支持的文件系统中的一种&#xff0c;它允许网络中的计算机之间通过TCP/IP网络共享资源在NFS的应用中的&#xff0c;本地的客户端应用可以透明的读写位于远端NFS服务器上的…

ELO排名算法

在wow lol 11平台等游戏中&#xff0c;都采用了一种排名算法&#xff0c;这种算法叫ELO&#xff0c;是一个叫ELO的人发明的&#xff0c;最开始用于国际象棋比赛计分。 Ra Ra K(Sa-Ea) 对于A来说&#xff0c;初始Rank值为Ra&#xff0c;一局结束后为 Ra。这里关键是K&#xff…

天梯匹配规则ELO

ELO Rating System 是当今对弈水平评估的公认权威规则&#xff0c;已被广泛应于国际象棋、围棋、足球和篮球等体育运动以及游戏中。例如星际争霸天梯排行&#xff0c;魔兽世界竞技场&#xff0c;Dota天梯系统&#xff0c;LOL匹配等游戏的竞技比赛系统中。ELO是一套较为完善的评…

欧拉算法的实现

代码 from matplotlib import pyplot as plt import numpy as np h 0.01 yy [] xx [] yy.append(1) xx.append(0) def y1(m,n): return m - 2*n/m for i in range(100): k1 y1(yy[i],xx[i]) y yy[i] hy1(yy[i],xx[i]) xx.append(xx[i]h) k2 y1(y,xx[i1]) yy.append(…

《机器学习算法竞赛实战》整理 | 八、实战案例:Elo Merchant Category Recommendation

详情请参见原书 ​​​​​《机器学习算法竞赛实战&#xff08;图灵出品&#xff09;》(王贺&#xff0c;刘鹏&#xff0c;钱乾)【摘要 书评 试读】- 京东图书 前言 比赛链接&#xff1a; https://www.kaggle.com/competitions/elo-merchant-category-recommendation/overvi…

ELK日志分析系统之ELK原理

目录 引言 一、ELK简介 1、ELK日志分组组成 Elasticsearch&#xff08;es&#xff09; Logstash Kibana 2、日志处理步骤 二、Elasticsearch详解 1、Elasticsearch概述 2、Elasticsearch核心概念 三、Logstash详解 1、Logstash的主要组件 四、Kibana详解 1、Kiba…

大数据技术ELK实时检索

一 elasticsearch简介 ElasticSearch是一个高性能&#xff0c;基于Lucene的全文检索服务&#xff0c;是一个分布式的Restful风格的搜索和数据分析引擎&#xff0c;也可以作为NoSQL数据库使用。 对Lucene进行了扩展 原型环境和生产环境可无缝切换 能够水平扩展 支持结构化和非结…