如何做自己的服务监控?spring boot 2.x服务监控揭秘

Actuatorspring boot项目中非常强大一个功能,有助于对应用程序进行监视和管理,通过 restful api请求来监管、审计、收集应用的运行情况,针对微服务而言它是必不可少的一个环节。

spring 2.x actuator相对于spring 1.x actuator做了较大的改变。

如何做自己的服务监控?spring boot 1.x服务监控揭秘

 ****提供了更多的endpoint管理

IDDescriptionEnabled by default

auditevents

Exposes audit events information for the current application.

Yes

beans

Displays a complete list of all the Spring beans in your application.

Yes

caches

Exposes available caches.

Yes

conditions

Shows the conditions that were evaluated on configuration and auto-configuration classes and the reasons why they did or did not match.

Yes

configprops

Displays a collated list of all @ConfigurationProperties.

Yes

env

Exposes properties from Spring’s ConfigurableEnvironment.

Yes

flyway

Shows any Flyway database migrations that have been applied.

Yes

health

Shows application health information.

Yes

httptrace

Displays HTTP trace information (by default, the last 100 HTTP request-response exchanges).

Yes

info

Displays arbitrary application info.

Yes

integrationgraph

Shows the Spring Integration graph.

Yes

loggers

Shows and modifies the configuration of loggers in the application.

Yes

liquibase

Shows any Liquibase database migrations that have been applied.

Yes

metrics

Shows ‘metrics’ information for the current application.

Yes

mappings

Displays a collated list of all @RequestMapping paths.

Yes

scheduledtasks

Displays the scheduled tasks in your application.

Yes

sessions

Allows retrieval and deletion of user sessions from a Spring Session-backed session store. Not available when using Spring Session’s support for reactive web applications.

Yes

shutdown

Lets the application be gracefully shutdown.

No

threaddump

Performs a thread dump.

Yes

If your application is a web application (Spring MVC, Spring WebFlux, or Jersey), you can use the following additional endpoints:

IDDescriptionEnabled by default

heapdump

Returns an hprof heap dump file.

Yes

jolokia

Exposes JMX beans over HTTP (when Jolokia is on the classpath, not available for WebFlux).

Yes

logfile

Returns the contents of the logfile (if logging.file or logging.path properties have been set). Supports the use of the HTTP Range header to retrieve part of the log file’s content.

Yes

prometheus

Exposes metrics in a format that can be scraped by a Prometheus server.

Yes

********更多的metrics

  • JVM metrics, report utilization of:

    • Various memory and buffer pools
    • Statistics related to garbage collection
    • Threads utilization
    • Number of classes loaded/unloaded
  • CPU metrics
  • File descriptor metrics
  • Kafka consumer metrics
  • Log4j2 metrics: record the number of events logged to Log4j2 at each level
  • Logback metrics: record the number of events logged to Logback at each level
  • Uptime metrics: report a gauge for uptime and a fixed gauge representing the application’s absolute start time
  • Tomcat metrics
  • Spring Integration metrics

*********内部实现也发生了较大的变化,使用micrometer第三方工具来完成metric的实现。下面我们就亲自看看吧!

1.准备源码

https://github.com/tjanusz-personal/springbootrestdemo/tree/master/src/main/java/com/demo/springbootrestdemo

2.导入并启动项目

( ( )\___ | '_ | '_| | '_ / _` |    
 \/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.0.4.RELEASE)

2019-01-17 11:20:39.076  INFO 5180 --- [           main] c.d.s.SpringbootrestdemoApplication      : Starting SpringbootrestdemoApplication on DESKTOP-405G2C8 with PID 5180 (E:documentspringbootrestdemouildclassesjavamain started by dell in E:documentspringbootrestdemo)
2019-01-17 11:20:39.080  INFO 5180 --- [           main] c.d.s.SpringbootrestdemoApplication      : No active profile set, falling back to default profiles: default
2019-01-17 11:20:39.147  INFO 5180 --- [           main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6f01b95f: startup date [Thu Jan 17 11:20:39 CST 2019]; root of context hierarchy
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.springframework.cglib.core.ReflectUtils$1 (file:/D:/gradle/caches/modules-2/files-2.1/org.springframework/spring-core/5.0.8.RELEASE/dc39c49e3246cdf73d3786ac41119140aed3fa08/spring-core-5.0.8.RELEASE.jar) to method java.lang.ClassLoader.defineClass(java.lang.Stri
ng,byte[],int,int,java.security.ProtectionDomain)
WARNING: Please consider reporting this to the maintainers of org.springframework.cglib.core.ReflectUtils$1
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
2019-01-17 11:20:40.341  INFO 5180 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.hateoas.config.HateoasConfiguration' of type [org.springframework.hateoas.config.HateoasConfiguration$$EnhancerBySpringCGLIB$$ecf2a812] is not eligible for getting processed by a
ll BeanPostProcessors (for example: not eligible for auto-proxying)
2019-01-17 11:20:40.982  INFO 5180 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2019-01-17 11:20:41.015  INFO 5180 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2019-01-17 11:20:41.016  INFO 5180 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.32
2019-01-17 11:20:41.029  INFO 5180 --- [ost-startStop-1] o.a.catalina.core.AprLifecycleListener   : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [D:softwarejdk11in;C:WindowsSunJavain;C:Windowssys
tem32;C:Windows;C:Windowssystem32;C:Windows;C:WindowsSystem32Wbem;C:WindowsSystem32WindowsPowerShellv1.0;C:WindowsSystem32OpenSSH;D:softwarejdk11in;D:softwareTortoiseGitin;D:softwareGitcmd;D:softwareapache-maven-3.5.4in;D:softwaregradle-4.10.2in;D:softwaregoin;D:s
oftwareantin;D:softwareanaconda;D:softwareanacondaScripts;D:softwareanacondaLibraryin;C:UsersdellAppDataLocalMicrosoftWindowsApps;;.]
2019-01-17 11:20:41.162  INFO 5180 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2019-01-17 11:20:41.168  INFO 5180 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 2035 ms
2019-01-17 11:20:41.832  INFO 5180 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]
2019-01-17 11:20:41.832  INFO 5180 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'webMvcMetricsFilter' to: [/*]
2019-01-17 11:20:41.832  INFO 5180 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpTraceFilter' to: [/*]
2019-01-17 11:20:41.833  INFO 5180 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Servlet dispatcherServlet mapped to [/]
2019-01-17 11:20:41.960  INFO 5180 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/saveUrl],methods=[POST],consumes=[application/json;charset=UTF-8 || application/x-www-form-urlencoded],produces=[application/json;charset=UTF-8 || application/xml]}" onto public com.demo.spring
bootrestdemo.domain.response.SaveUrlResponse com.demo.springbootrestdemo.web.DemoController.saveUrl(com.demo.springbootrestdemo.domain.request.SaveUrlRequest,java.lang.String,java.lang.String)
2019-01-17 11:20:41.961  INFO 5180 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/alive],methods=[GET]}" onto public com.demo.springbootrestdemo.domain.response.DemoPingResponse com.demo.springbootrestdemo.web.DemoController.alive()
2019-01-17 11:20:41.964  INFO 5180 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http
.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2019-01-17 11:20:41.964  INFO 5180 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.err
or(javax.servlet.http.HttpServletRequest)
2019-01-17 11:20:42.038  INFO 5180 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6f01b95f: startup date [Thu Jan 17 11:20:39 CST 2019]; root of context hierar
chy
2019-01-17 11:20:42.087  INFO 5180 --- [           main] .m.m.a.ExceptionHandlerExceptionResolver : Detected @ExceptionHandler methods in restResponseEntityExceptionHandler
2019-01-17 11:20:42.529  INFO 5180 --- [           main] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 14 endpoint(s) beneath base path '/actuator'
2019-01-17 11:20:42.538  INFO 5180 --- [           main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator/auditevents],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.web.serv
let.AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)
2019-01-17 11:20:42.539  INFO 5180 --- [           main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator/beans],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.web.servlet.Ab
stractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)
2019-01-17 11:20:42.539  INFO 5180 --- [           main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator/health],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.web.servlet.A
bstractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)
2019-01-17 11:20:42.540  INFO 5180 --- [           main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator/conditions],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.web.servl
et.AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)
2019-01-17 11:20:42.540  INFO 5180 --- [           main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator/configprops],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.web.serv
let.AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)
2019-01-17 11:20:42.540  INFO 5180 --- [           main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator/env/{toMatch}],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.web.se
rvlet.AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)
2019-01-17 11:20:42.541  INFO 5180 --- [           main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator/env],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.web.servlet.Abst
ractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)
2019-01-17 11:20:42.541  INFO 5180 --- [           main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator/info],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.web.servlet.Abs
tractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)
2019-01-17 11:20:42.541  INFO 5180 --- [           main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator/loggers/{name}],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.web.s
ervlet.AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)
2019-01-17 11:20:42.542  INFO 5180 --- [           main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator/loggers/{name}],methods=[POST],consumes=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.web.
servlet.AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)
2019-01-17 11:20:42.542  INFO 5180 --- [           main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator/loggers],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.web.servlet.
AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)
2019-01-17 11:20:42.543  INFO 5180 --- [           main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator/heapdump],methods=[GET],produces=[application/octet-stream]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$Op
erationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)
2019-01-17 11:20:42.543  INFO 5180 --- [           main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator/threaddump],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.web.servl
et.AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)
2019-01-17 11:20:42.543  INFO 5180 --- [           main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator/metrics],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.web.servlet.
AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)
2019-01-17 11:20:42.544  INFO 5180 --- [           main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator/metrics/{requiredMetricName}],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.
endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)
2019-01-17 11:20:42.544  INFO 5180 --- [           main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator/scheduledtasks],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.web.s
ervlet.AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)
2019-01-17 11:20:42.544  INFO 5180 --- [           main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator/httptrace],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.web.servle
t.AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)
2019-01-17 11:20:42.545  INFO 5180 --- [           main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator/mappings],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.web.servlet
.AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)
2019-01-17 11:20:42.546  INFO 5180 --- [           main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto protected java.util.Map<java.lang.String, java.util.Map<java.lang.String, org.springfr
amework.boot.actuate.endpoint.web.Link>> org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping.links(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2019-01-17 11:20:42.590  INFO 5180 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2019-01-17 11:20:42.641  INFO 5180 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2019-01-17 11:20:42.648  INFO 5180 --- [           main] c.d.s.SpringbootrestdemoApplication      : Started SpringbootrestdemoApplication in 4.008 seconds (JVM running for 4.517)
<=========----> 75% EXECUTING [25s]
> :bootRun

3.获取EndPoint

3.0 来源

        "webEndpointServletHandlerMapping": {
          "aliases": [
            
          ],
          "scope": "singleton",
          "type": "org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping",
          "resource": "class path resource [org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/WebMvcEndpointManagementContextConfiguration.class]",
          "dependencies": [
            "webEndpointDiscoverer",
            "servletEndpointDiscoverer",
            "controllerEndpointDiscoverer",
            "endpointMediaTypes",
            "management.endpoints.web.cors-org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties",
            "management.endpoints.web-org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties"
          ]
        }

 spring.factories定义了

org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration=
org.springframework.boot.actuate.autoconfigure.endpoint.web.ServletEndpointManagementContextConfiguration,
org.springframework.boot.actuate.autoconfigure.endpoint.web.reactive.WebFluxEndpointManagementContextConfiguration,
org.springframework.boot.actuate.autoconfigure.endpoint.web.servlet.WebMvcEndpointManagementContextConfiguration,
org.springframework.boot.actuate.autoconfigure.endpoint.web.jersey.JerseyWebEndpointManagementContextConfiguration,
org.springframework.boot.actuate.autoconfigure.web.jersey.JerseyManagementChildContextConfiguration,
org.springframework.boot.actuate.autoconfigure.web.reactive.ReactiveManagementChildContextConfiguration,
org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementChildContextConfiguration,
org.springframework.boot.actuate.autoconfigure.web.servlet.WebMvcEndpointChildContextConfiguration

3.1 映射

    @Bean
    @ConditionalOnMissingBean
    public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(
            WebEndpointsSupplier webEndpointsSupplier,
            ServletEndpointsSupplier servletEndpointsSupplier,
            ControllerEndpointsSupplier controllerEndpointsSupplier,
            EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties,
            WebEndpointProperties webEndpointProperties) {
        List<ExposableEndpoint<?>> allEndpoints = new ArrayList<>();
        Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier
                .getEndpoints();
        allEndpoints.addAll(webEndpoints);
        allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
        allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
        EndpointMapping endpointMapping = new EndpointMapping(
                webEndpointProperties.getBasePath());
        return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints,
                endpointMediaTypes, corsProperties.toCorsConfiguration(),
                new EndpointLinksResolver(allEndpoints,
                        webEndpointProperties.getBasePath()));
    }

3.2 获取EndPoint

3.2.1 EndPoint定义

/**
 * Identifies a type as being an actuator endpoint that provides information about the
 * running application. Endpoints can be exposed over a variety of technologies including
 * JMX and HTTP.
 * <p>
 * Most {@code @Endpoint} classes will declare one or more
 * {@link ReadOperation @ReadOperation}, {@link WriteOperation @WriteOperation},
 * {@link DeleteOperation @DeleteOperation} annotated methods which will be automatically
 * adapted to the exposing technology (JMX, Spring MVC, Spring WebFlux, Jersey etc.).
 * <p>
 * {@code @Endpoint} represents the lowest common denominator for endpoints and
 * intentionally limits the sorts of operation methods that may be defined in order to
 * support the broadest possible range of exposure technologies. If you need deeper
 * support for a specific technology you can either write an endpoint that is
 * {@link FilteredEndpoint filtered} to a certain technology, or provide
 * {@link EndpointExtension extension} for the broader endpoint.
 *
 * @author Andy Wilkinson
 * @author Phillip Webb
 * @since 2.0.0
 * @see EndpointExtension
 * @see FilteredEndpoint
 * @see EndpointDiscoverer
 */

使用了@Endpoint注解的类

 3.2.2 获取所有的EndPoint

    @Override
    public final Collection<E> getEndpoints() {
        if (this.endpoints == null) {
            this.endpoints = discoverEndpoints();
        }
        return this.endpoints;
    }

    private Collection<E> discoverEndpoints() {
        Collection<EndpointBean> endpointBeans = createEndpointBeans();
        addExtensionBeans(endpointBeans);
        return convertToEndpoints(endpointBeans);
    }

    private Collection<EndpointBean> createEndpointBeans() {
        Map<EndpointId, EndpointBean> byId = new LinkedHashMap<>();
        String[] beanNames = BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors(
                this.applicationContext, Endpoint.class);
        for (String beanName : beanNames) {
            if (!ScopedProxyUtils.isScopedTarget(beanName)) {
                EndpointBean endpointBean = createEndpointBean(beanName);
                EndpointBean previous = byId.putIfAbsent(endpointBean.getId(),
                        endpointBean);
                Assert.state(previous == null,
                        () -> "Found two endpoints with the id '" + endpointBean.getId()
                                + "': '" + endpointBean.getBeanName() + "' and '"
                                + previous.getBeanName() + "'");
            }
        }
        return byId.values();
    }

    private EndpointBean createEndpointBean(String beanName) {
        Object bean = this.applicationContext.getBean(beanName);
        return new EndpointBean(beanName, bean);
    }

    private void addExtensionBeans(Collection<EndpointBean> endpointBeans) {
        Map<EndpointId, EndpointBean> byId = endpointBeans.stream()
                .collect(Collectors.toMap(EndpointBean::getId, Function.identity()));
        String[] beanNames = BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors(
                this.applicationContext, EndpointExtension.class);
        for (String beanName : beanNames) {
            ExtensionBean extensionBean = createExtensionBean(beanName);
            EndpointBean endpointBean = byId.get(extensionBean.getEndpointId());
            Assert.state(endpointBean != null,
                    () -> ("Invalid extension '" + extensionBean.getBeanName()
                            + "': no endpoint found with id '"
                            + extensionBean.getEndpointId() + "'"));
            addExtensionBean(endpointBean, extensionBean);
        }
    }

3.3 WebMvcEndpointHandlerMapping

3.3.0 spring mvc的流程复习

其中:

 第2步获取HandlerMapping,其层次结构

其中RequestMappingHandlerMapping用来处理@RequestMapping注解

    private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
        RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
        RequestCondition<?> condition = (element instanceof Class ?
                getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
        return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
    }

第3步获取HandlerAdapter,其层次结构

 

默认handlerMapping和handlerAdapter定义在DispatcherServlet.properties

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,
    org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

3.3.1 获取WebMvcEndpointHandlerMapping

    /**
     * Creates a new {@code WebMvcEndpointHandlerMapping} instance that provides mappings
     * for the given endpoints.
     * @param endpointMapping the base mapping for all endpoints
     * @param endpoints the web endpoints
     * @param endpointMediaTypes media types consumed and produced by the endpoints
     * @param corsConfiguration the CORS configuration for the endpoints or {@code null}
     * @param linksResolver resolver for determining links to available endpoints
     */
    public WebMvcEndpointHandlerMapping(EndpointMapping endpointMapping,
            Collection<ExposableWebEndpoint> endpoints,
            EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration,
            EndpointLinksResolver linksResolver) {
        super(endpointMapping, endpoints, endpointMediaTypes, corsConfiguration);
        this.linksResolver = linksResolver;
        setOrder(-100);
    }

父类实现了initHandlerMethods()

    @Override
    protected void initHandlerMethods() {
        for (ExposableWebEndpoint endpoint : this.endpoints) {
            for (WebOperation operation : endpoint.getOperations()) {
                registerMappingForOperation(endpoint, operation);
            }
        }
        if (StringUtils.hasText(this.endpointMapping.getPath())) {
            registerLinksMapping();
        }
    }

将endpoint的Operation映射到Method级别,类似于@RequestMapping

    private void registerMappingForOperation(ExposableWebEndpoint endpoint,
            WebOperation operation) {
        ServletWebOperation servletWebOperation = wrapServletWebOperation(endpoint,
                operation, new ServletWebOperationAdapter(operation));
        registerMapping(createRequestMappingInfo(operation),
                new OperationHandler(servletWebOperation), this.handleMethod);
    }

    /**
     * Hook point that allows subclasses to wrap the {@link ServletWebOperation} before
     * it's called. Allows additional features, such as security, to be added.
     * @param endpoint the source endpoint
     * @param operation the source operation
     * @param servletWebOperation the servlet web operation to wrap
     * @return a wrapped servlet web operation
     */
    protected ServletWebOperation wrapServletWebOperation(ExposableWebEndpoint endpoint,
            WebOperation operation, ServletWebOperation servletWebOperation) {
        return servletWebOperation;
    }

    private RequestMappingInfo createRequestMappingInfo(WebOperation operation) {
        WebOperationRequestPredicate predicate = operation.getRequestPredicate();
        PatternsRequestCondition patterns = patternsRequestConditionForPattern(
                predicate.getPath());
        RequestMethodsRequestCondition methods = new RequestMethodsRequestCondition(
                RequestMethod.valueOf(predicate.getHttpMethod().name()));
        ConsumesRequestCondition consumes = new ConsumesRequestCondition(
                StringUtils.toStringArray(predicate.getConsumes()));
        ProducesRequestCondition produces = new ProducesRequestCondition(
                StringUtils.toStringArray(predicate.getProduces()));
        return new RequestMappingInfo(null, patterns, methods, null, null, consumes,
                produces, null);
    }

 3.3.2 获取ServletWebOperationAdapter 

AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle

        @Override
        public Object handle(HttpServletRequest request,
                @RequestBody(required = false) Map<String, String> body) {
            Map<String, Object> arguments = getArguments(request, body);
            try {
                return handleResult(
                        this.operation.invoke(new InvocationContext(
                                new ServletSecurityContext(request), arguments)),
                        HttpMethod.valueOf(request.getMethod()));
            }
            catch (InvalidEndpointRequestException ex) {
                throw new BadOperationRequestException(ex.getReason());
            }
        }

4.触发机制

If you add a @Bean annotated with @Endpoint, any methods annotated with @ReadOperation@WriteOperation, or @DeleteOperation are automatically exposed over JMX and, in a web application, over HTTP as well. Endpoints can be exposed over HTTP using Jersey, Spring MVC, or Spring WebFlux.

if you need access to web-framework-specific functionality, you can implement Servlet or Spring @Controller and @RestController endpoints at the cost of them not being available over JMX or when using a different web framework.

EndpointDiscoverer

    @Override
    public final Collection<E> getEndpoints() {
        if (this.endpoints == null) {
            this.endpoints = discoverEndpoints();
        }
        return this.endpoints;
    }

    private Collection<E> discoverEndpoints() {
        Collection<EndpointBean> endpointBeans = createEndpointBeans();
        addExtensionBeans(endpointBeans);
        return convertToEndpoints(endpointBeans);
    }

调用 @ReadOperation@WriteOperation, or @DeleteOperation方法

    private void addOperations(MultiValueMap<OperationKey, O> indexed, EndpointId id,
            Object target, boolean replaceLast) {
        Set<OperationKey> replacedLast = new HashSet<>();
        Collection<O> operations = this.operationsFactory.createOperations(id, target);
        for (O operation : operations) {
            OperationKey key = createOperationKey(operation);
            O last = getLast(indexed.get(key));
            if (replaceLast && replacedLast.add(key) && last != null) {
                indexed.get(key).remove(last);
            }
            indexed.add(key, operation);
        }
    }

其中DiscoveredOperationsFactory定义了@ReadOperation@WriteOperation, or @DeleteOperation方法

    private static final Map<OperationType, Class<? extends Annotation>> OPERATION_TYPES;

    static {
        Map<OperationType, Class<? extends Annotation>> operationTypes = new EnumMap<>(
                OperationType.class);
        operationTypes.put(OperationType.READ, ReadOperation.class);
        operationTypes.put(OperationType.WRITE, WriteOperation.class);
        operationTypes.put(OperationType.DELETE, DeleteOperation.class);
        OPERATION_TYPES = Collections.unmodifiableMap(operationTypes);
    }

    private final ParameterValueMapper parameterValueMapper;

    private final Collection<OperationInvokerAdvisor> invokerAdvisors;

    DiscoveredOperationsFactory(ParameterValueMapper parameterValueMapper,
            Collection<OperationInvokerAdvisor> invokerAdvisors) {
        this.parameterValueMapper = parameterValueMapper;
        this.invokerAdvisors = invokerAdvisors;
    }

    public Collection<O> createOperations(EndpointId id, Object target) {
        return MethodIntrospector.selectMethods(target.getClass(),
                (MetadataLookup<O>) (method) -> createOperation(id, target, method))
                .values();
    }

    private O createOperation(EndpointId endpointId, Object target, Method method) {
        return OPERATION_TYPES.entrySet().stream()
                .map((entry) -> createOperation(endpointId, target, method,
                        entry.getKey(), entry.getValue()))
                .filter(Objects::nonNull).findFirst().orElse(null);
    }

总结:

1.若一个Bean在类上使用@Endpoint注解,并且在方法上使用@ReadOperation@WriteOperation, or @DeleteOperation注解,那么自动可以使用jmx或者http访问,http提供服务的可以是Jersey, Spring MVC或者Spring WebFlux.

2.http提供服务的基本机制

2.1 使用HandlerMapping映射请求
2.2 使用HandlerAdapter(自定义)聚合EndPoint,并代理EndPoint的请求。

参考文献

【1】https://terasolunaorg.github.io/guideline/1.0.x/en/Overview/SpringMVCOverview.html

【2】https://docs.spring.io/spring-boot/docs/2.1.2.RELEASE/reference/htmlsingle/#production-ready-enabling

原文地址:https://www.cnblogs.com/davidwang456/p/10281420.html