来源于spring-boot-actuator的启示,actuator中提供了很多端点,我们只需要引入响应的依赖,监控服务即可访问我们的应用,获取到响应的信息,例如http://127.0.0.1:8082/actuator/health,那么它是如何将这些服务发布出去的呢,让我们一起探究一下它的原理。Actuator有很多端点,原理都是一样的,常用端点有auditevents、beans、caches、conditions 、configprops、flyway 、env 、health 、httptrace 、info、loggers、liquibase、metrics、mappings、scheduledtasks、sessions、shutdown 、threaddump 等。
SpringMVC的请求处理器
1、spring中处理servlet请求是通过org.springframework.web.servlet.DispatcherServlet类来完成的。
2、Spring 容器启动加载刷新DispatcherServlet类时,回从容器中获取所有的HandlerMapping类型的Bean,存放在List<HandlerMapping>类型的变量中:handlerMappings。
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
//获取所有HandlerMapping类型的Bean放入Map中
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
......
}
......
}
3、HandlerMapping中注册了请求路径和处理请求的方法的映射关系,例如我们常用的Controller类里的方法上@RequestMapping注解的value值和该方法的映射。
4、当DispatcherServlet收到请求后,会遍历上述HandlerMapping列表的HandlerMapping,HandlerMapping根据request的请求路径,尝试匹配一个处理方法,如果匹配到就使用该方法处理请求。
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
//遍历HandlerMapping列表根据request路径匹配handler
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
5、actuator就是根据上面的原理自定义了一个HandlerMapping,在程序启动时被加载到了DispatcherServlet的handlerMappings列表中。
Actuator定义端点的方法
1、在提供端点的类上使用org.springframework.boot.actuate.endpoint.annotation.Endpoint
注解。
2、在方法上添加注解@ReadOperation(读操作)、@WriteOperation
(写操作)、@DeleteOperation(删除操作)
@Endpoint(id = "health")
public class HealthEndpoint extends HealthEndpointSupport<HealthContributor, HealthComponent> {
......
@ReadOperation
public HealthComponent health() {
HealthComponent health = health(ApiVersion.V3, EMPTY_PATH);
return (health != null) ? health : DEFAULT_HEALTH;
}
......
}
Actuator的实现HandlerMapping的类
org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping
1、当满足自动配置actuator的条件时会启动自动配置WebMvcEndpointManagementContextConfiguration
2、上述自动配置会构建WebMvcEndpointHandlerMapping的Bean对象。
3、构建对象的过程会从Spring容器中得到所有的注解了Endpoint的类和方法信息,传给WebMvcEndpointHandlerMapping对象,用于建立请求路径和处理方法的映射关系。
@Bean
@ConditionalOnMissingBean
public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier,
ServletEndpointsSupplier servletEndpointsSupplier, ControllerEndpointsSupplier controllerEndpointsSupplier,
EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties,
WebEndpointProperties webEndpointProperties, Environment environment) {
List<ExposableEndpoint<?>> allEndpoints = new ArrayList<>();
Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();
//添加不同类型的端点,webEndpoint对应这篇文章的端点类型
allEndpoints.addAll(webEndpoints);
allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
String basePath = webEndpointProperties.getBasePath();
EndpointMapping endpointMapping = new EndpointMapping(basePath);
boolean shouldRegisterLinksMapping = StringUtils.hasText(basePath)
|| ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT);
//创建HandlerMapping类型的Bean,会被加载到DispatcherServlet的handlerMappings中,用于匹配请求
//webEndpoints中包含扫描到的具有EndPoint注解的类的信息,用于创建映射关系
return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes,
corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath),
shouldRegisterLinksMapping);
如果我们在提供公共组件或客户端时,需要和外部服务交互,我们可以仿照actuator的做法,公布自己的端点。