Actuator
是spring boot
项目中非常强大一个功能,有助于对应用程序进行监视和管理,通过 restful api
请求来监管、审计、收集应用的运行情况,针对微服务而言它是必不可少的一个环节。
spring 2.x actuator相对于spring 1.x actuator做了较大的改变。
如何做自己的服务监控?spring boot 1.x服务监控揭秘
****提供了更多的endpoint管理
ID | Description | Enabled by default |
---|---|---|
|
Exposes audit events information for the current application. |
Yes |
|
Displays a complete list of all the Spring beans in your application. |
Yes |
|
Exposes available caches. |
Yes |
|
Shows the conditions that were evaluated on configuration and auto-configuration classes and the reasons why they did or did not match. |
Yes |
|
Displays a collated list of all |
Yes |
|
Exposes properties from Spring’s |
Yes |
|
Shows any Flyway database migrations that have been applied. |
Yes |
|
Shows application health information. |
Yes |
|
Displays HTTP trace information (by default, the last 100 HTTP request-response exchanges). |
Yes |
|
Displays arbitrary application info. |
Yes |
|
Shows the Spring Integration graph. |
Yes |
|
Shows and modifies the configuration of loggers in the application. |
Yes |
|
Shows any Liquibase database migrations that have been applied. |
Yes |
|
Shows ‘metrics’ information for the current application. |
Yes |
|
Displays a collated list of all |
Yes |
|
Displays the scheduled tasks in your application. |
Yes |
|
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 |
|
Lets the application be gracefully shutdown. |
No |
|
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:
ID | Description | Enabled by default |
---|---|---|
|
Returns an |
Yes |
|
Exposes JMX beans over HTTP (when Jolokia is on the classpath, not available for WebFlux). |
Yes |
|
Returns the contents of the logfile (if |
Yes |
|
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:\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
微信公众号: 架构师日常笔记 欢迎关注!