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

"

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

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

****提供了更多的endpoint管理《略》

********更多的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:\document\springbootrestdemo\build\classes\java\main started by dell in E:\document\springbootrestdemo)
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:\software\jdk11\bin;C:\Windows\Sun\Java\bin;C:\Windows\sys
tem32;C:\Windows;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;D:\software\jdk11\bin;D:\software\TortoiseGit\bin;D:\software\Git\cmd;D:\software\apache-maven-3.5.4\bin;D:\software\gradle-4.10.2\bin;D:\software\go\bin;D:\s
oftware\ant\bin;D:\software\anaconda;D:\software\anaconda\Scripts;D:\software\anaconda\Library\bin;C:\Users\dell\AppData\Local\Microsoft\WindowsApps;;.]
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

"
发表评论
留言与评论(共有 0 条评论)
   
验证码:

相关文章

推荐文章

'); })();