spring boot actuator工作原理之http服务暴露源码分析
spring boot actuator的官方文档地址:https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready.html
1.增加actuator支持
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
2.修改配置
示例:
endpoints.actuator.enabled=true
endpoints.actuator.sensitive=false
endpoints.beans.sensitive=false
endpoints.beans.enabled=true
endpoints.health.sensitive=false
endpoints.health.enabled=true
management.security.enabled=false
红色部分重要,默认是需要身份认证的,一些页面不能访问,加上后所有页面不需要认证,都可以访问。
3.启动效果如下:
2017-04-07 14:42:46.569 INFO 10912 --- [ 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.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2017-04-07 14:42:46.569 INFO 10912 --- [ 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.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2017-04-07 14:42:46.621 INFO 10912 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@3224bdee: startup date [Fri Apr 07 14:42:43 CST 2017]; root of context hierarchy
2017-04-07 14:42:47.127 INFO 10912 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/metrics/{name:.*}],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint.value(java.lang.String)
2017-04-07 14:42:47.127 INFO 10912 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/metrics || /metrics.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2017-04-07 14:42:47.128 INFO 10912 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/mappings || /mappings.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2017-04-07 14:42:47.128 INFO 10912 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/env/{name:.*}],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint.value(java.lang.String)
2017-04-07 14:42:47.128 INFO 10912 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/env || /env.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2017-04-07 14:42:47.129 INFO 10912 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/configprops || /configprops.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2017-04-07 14:42:47.129 INFO 10912 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/dump || /dump.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2017-04-07 14:42:47.129 INFO 10912 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/health || /health.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint.invoke(javax.servlet.http.HttpServletRequest)
2017-04-07 14:42:47.129 INFO 10912 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/trace || /trace.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2017-04-07 14:42:47.130 INFO 10912 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/autoconfig || /autoconfig.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2017-04-07 14:42:47.130 INFO 10912 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/info || /info.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2017-04-07 14:42:47.131 INFO 10912 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/auditevents || /auditevents.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public org.springframework.http.ResponseEntity<?> org.springframework.boot.actuate.endpoint.mvc.AuditEventsMvcEndpoint.findByPrincipalAndAfterAndType(java.lang.String,java.util.Date,java.lang.String)
2017-04-07 14:42:47.131 INFO 10912 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/heapdump || /heapdump.json],methods=[GET],produces=[application/octet-stream]}" onto public void org.springframework.boot.actuate.endpoint.mvc.HeapdumpMvcEndpoint.invoke(boolean,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws java.io.IOException,javax.servlet.ServletException
2017-04-07 14:42:47.132 INFO 10912 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/loggers/{name:.*}],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.LoggersMvcEndpoint.get(java.lang.String)
2017-04-07 14:42:47.132 INFO 10912 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/loggers/{name:.*}],methods=[POST],consumes=[application/vnd.spring-boot.actuator.v1+json || application/json],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.LoggersMvcEndpoint.set(java.lang.String,java.util.Map<java.lang.String, java.lang.String>)
2017-04-07 14:42:47.132 INFO 10912 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/loggers || /loggers.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2017-04-07 14:42:47.133 INFO 10912 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/beans || /beans.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2017-04-07 14:42:47.247 INFO 10912 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2017-04-07 14:42:47.248 INFO 10912 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Bean with name 'dataSource' has been autodetected for JMX exposure
2017-04-07 14:42:47.251 INFO 10912 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Located MBean 'dataSource': registering with JMX server as MBean [com.zaxxer.hikari:name=dataSource,type=HikariDataSource]
2017-04-07 14:42:47.256 INFO 10912 --- [ main] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase 0
2017-04-07 14:42:47.326 INFO 10912 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2017-04-07 14:42:47.330 INFO 10912 --- [ main] xxx.xxx.xxx.Application : Started Application in 3.642 seconds (JVM running for 6.678)
2017-04-07 14:43:05.269 INFO 10912 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet'
2017-04-07 14:43:05.269 INFO 10912 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started
2017-04-07 14:43:05.282 INFO 10912 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 13 ms
4.工作原理分析
4.1 EndpointHandlerMapping
从上述日志中,我们可以看到映射是由EndpointHandlerMapping完成的。我们看一下EndpointHandlerMapping的定义:
/**
* {@link HandlerMapping} to map {@link Endpoint}s to URLs via {@link Endpoint#getId()}.
* The semantics of {@code @RequestMapping} should be identical to a normal
* {@code @Controller}, but the endpoints should not be annotated as {@code @Controller}
* (otherwise they will be mapped by the normal MVC mechanisms).
* <p>
* One of the aims of the mapping is to support endpoints that work as HTTP endpoints but
* can still provide useful service interfaces when there is no HTTP server (and no Spring
* MVC on the classpath). Note that any endpoints having method signatures will break in a
* non-servlet environment.
*
* @author Phillip Webb
* @author Christian Dupuis
* @author Dave Syer
*/
public class EndpointHandlerMapping extends AbstractEndpointHandlerMapping<MvcEndpoint> { /**
* Create a new {@link EndpointHandlerMapping} instance. All {@link Endpoint}s will be
* detected from the {@link ApplicationContext}. The endpoints will not accept CORS
* requests.
* @param endpoints the endpoints
*/
public EndpointHandlerMapping(Collection<? extends MvcEndpoint> endpoints) {
super(endpoints);
} /**
* Create a new {@link EndpointHandlerMapping} instance. All {@link Endpoint}s will be
* detected from the {@link ApplicationContext}. The endpoints will accepts CORS
* requests based on the given {@code corsConfiguration}.
* @param endpoints the endpoints
* @param corsConfiguration the CORS configuration for the endpoints
* @since 1.3.0
*/
public EndpointHandlerMapping(Collection<? extends MvcEndpoint> endpoints,
CorsConfiguration corsConfiguration) {
super(endpoints, corsConfiguration);
} }
4.2 EndpointWebMvcManagementContextConfiguration
EndpointHandlerMapping从哪里来的呢?EndpointWebMvcManagementContextConfiguration定义了EndpointHandlerMapping:
/**
* Configuration to expose {@link Endpoint} instances over Spring MVC.
*
* @author Dave Syer
* @author Ben Hale
* @author Vedran Pavic
* @since 1.3.0
*/
@ManagementContextConfiguration
@EnableConfigurationProperties({ HealthMvcEndpointProperties.class,
EndpointCorsProperties.class })
public class EndpointWebMvcManagementContextConfiguration { private final HealthMvcEndpointProperties healthMvcEndpointProperties; private final ManagementServerProperties managementServerProperties; private final EndpointCorsProperties corsProperties; private final List<EndpointHandlerMappingCustomizer> mappingCustomizers; public EndpointWebMvcManagementContextConfiguration(
HealthMvcEndpointProperties healthMvcEndpointProperties,
ManagementServerProperties managementServerProperties,
EndpointCorsProperties corsProperties,
ObjectProvider<List<EndpointHandlerMappingCustomizer>> mappingCustomizers) {
this.healthMvcEndpointProperties = healthMvcEndpointProperties;
this.managementServerProperties = managementServerProperties;
this.corsProperties = corsProperties;
List<EndpointHandlerMappingCustomizer> providedCustomizers = mappingCustomizers
.getIfAvailable();
this.mappingCustomizers = providedCustomizers == null
? Collections.<EndpointHandlerMappingCustomizer>emptyList()
: providedCustomizers;
} @Bean
@ConditionalOnMissingBean
public EndpointHandlerMapping endpointHandlerMapping() {
Set<MvcEndpoint> endpoints = mvcEndpoints().getEndpoints();
CorsConfiguration corsConfiguration = getCorsConfiguration(this.corsProperties);
EndpointHandlerMapping mapping = new EndpointHandlerMapping(endpoints,
corsConfiguration);
mapping.setPrefix(this.managementServerProperties.getContextPath());
MvcEndpointSecurityInterceptor securityInterceptor = new MvcEndpointSecurityInterceptor(
this.managementServerProperties.getSecurity().isEnabled(),
this.managementServerProperties.getSecurity().getRoles());
mapping.setSecurityInterceptor(securityInterceptor);
for (EndpointHandlerMappingCustomizer customizer : this.mappingCustomizers) {
customizer.customize(mapping);
}
return mapping;
} private CorsConfiguration getCorsConfiguration(EndpointCorsProperties properties) {
if (CollectionUtils.isEmpty(properties.getAllowedOrigins())) {
return null;
}
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(properties.getAllowedOrigins());
if (!CollectionUtils.isEmpty(properties.getAllowedHeaders())) {
configuration.setAllowedHeaders(properties.getAllowedHeaders());
}
if (!CollectionUtils.isEmpty(properties.getAllowedMethods())) {
configuration.setAllowedMethods(properties.getAllowedMethods());
}
if (!CollectionUtils.isEmpty(properties.getExposedHeaders())) {
configuration.setExposedHeaders(properties.getExposedHeaders());
}
if (properties.getMaxAge() != null) {
configuration.setMaxAge(properties.getMaxAge());
}
if (properties.getAllowCredentials() != null) {
configuration.setAllowCredentials(properties.getAllowCredentials());
}
return configuration;
} @Bean
@ConditionalOnMissingBean
public MvcEndpoints mvcEndpoints() {
return new MvcEndpoints();
} @Bean
@ConditionalOnBean(EnvironmentEndpoint.class)
@ConditionalOnEnabledEndpoint("env")
public EnvironmentMvcEndpoint environmentMvcEndpoint(EnvironmentEndpoint delegate) {
return new EnvironmentMvcEndpoint(delegate);
} @Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint("heapdump")
public HeapdumpMvcEndpoint heapdumpMvcEndpoint() {
return new HeapdumpMvcEndpoint();
} @Bean
@ConditionalOnBean(HealthEndpoint.class)
@ConditionalOnEnabledEndpoint("health")
public HealthMvcEndpoint healthMvcEndpoint(HealthEndpoint delegate,
ManagementServerProperties managementServerProperties) {
HealthMvcEndpoint healthMvcEndpoint = new HealthMvcEndpoint(delegate,
this.managementServerProperties.getSecurity().isEnabled(),
managementServerProperties.getSecurity().getRoles());
if (this.healthMvcEndpointProperties.getMapping() != null) {
healthMvcEndpoint
.addStatusMapping(this.healthMvcEndpointProperties.getMapping());
}
return healthMvcEndpoint;
} @Bean
@ConditionalOnBean(LoggersEndpoint.class)
@ConditionalOnEnabledEndpoint("loggers")
public LoggersMvcEndpoint loggersMvcEndpoint(LoggersEndpoint delegate) {
return new LoggersMvcEndpoint(delegate);
} @Bean
@ConditionalOnBean(MetricsEndpoint.class)
@ConditionalOnEnabledEndpoint("metrics")
public MetricsMvcEndpoint metricsMvcEndpoint(MetricsEndpoint delegate) {
return new MetricsMvcEndpoint(delegate);
} @Bean
@ConditionalOnEnabledEndpoint("logfile")
@Conditional(LogFileCondition.class)
public LogFileMvcEndpoint logfileMvcEndpoint() {
return new LogFileMvcEndpoint();
} @Bean
@ConditionalOnBean(ShutdownEndpoint.class)
@ConditionalOnEnabledEndpoint(value = "shutdown", enabledByDefault = false)
public ShutdownMvcEndpoint shutdownMvcEndpoint(ShutdownEndpoint delegate) {
return new ShutdownMvcEndpoint(delegate);
} @Bean
@ConditionalOnBean(AuditEventRepository.class)
@ConditionalOnEnabledEndpoint("auditevents")
public AuditEventsMvcEndpoint auditEventMvcEndpoint(
AuditEventRepository auditEventRepository) {
return new AuditEventsMvcEndpoint(auditEventRepository);
} private static class LogFileCondition extends SpringBootCondition { @Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String config = environment.resolvePlaceholders("${logging.file:}");
ConditionMessage.Builder message = ConditionMessage.forCondition("Log File");
if (StringUtils.hasText(config)) {
return ConditionOutcome
.match(message.found("logging.file").items(config));
}
config = environment.resolvePlaceholders("${logging.path:}");
if (StringUtils.hasText(config)) {
return ConditionOutcome
.match(message.found("logging.path").items(config));
}
config = new RelaxedPropertyResolver(environment, "endpoints.logfile.")
.getProperty("external-file");
if (StringUtils.hasText(config)) {
return ConditionOutcome.match(
message.found("endpoints.logfile.external-file").items(config));
}
return ConditionOutcome.noMatch(message.didNotFind("logging file").atAll());
} } }
红色部分:
4.2.1.获取endpoint,Set<MvcEndpoint> endpoints = mvcEndpoints().getEndpoints();
方法如下:
@Override
public void afterPropertiesSet() throws Exception {
Collection<MvcEndpoint> existing = BeanFactoryUtils
.beansOfTypeIncludingAncestors(this.applicationContext, MvcEndpoint.class)
.values();
this.endpoints.addAll(existing);
this.customTypes = findEndpointClasses(existing);
@SuppressWarnings("rawtypes")
Collection<Endpoint> delegates = BeanFactoryUtils
.beansOfTypeIncludingAncestors(this.applicationContext, Endpoint.class)
.values();
for (Endpoint<?> endpoint : delegates) {
if (isGenericEndpoint(endpoint.getClass()) && endpoint.isEnabled()) {
EndpointMvcAdapter adapter = new EndpointMvcAdapter(endpoint);
String path = determinePath(endpoint,
this.applicationContext.getEnvironment());
if (path != null) {
adapter.setPath(path);
}
this.endpoints.add(adapter);
}
}
}
获取容器中的MvcEndpoint接口实现类。
4.2.2.实例化EndpointHandlerMapping
EndpointHandlerMapping mapping = new EndpointHandlerMapping(endpoints,
corsConfiguration);
创建实例
/**
* Create a new {@link EndpointHandlerMapping} instance. All {@link Endpoint}s will be
* detected from the {@link ApplicationContext}. The endpoints will accepts CORS
* requests based on the given {@code corsConfiguration}.
* @param endpoints the endpoints
* @param corsConfiguration the CORS configuration for the endpoints
* @since 1.3.0
*/
public EndpointHandlerMapping(Collection<? extends MvcEndpoint> endpoints,
CorsConfiguration corsConfiguration) {
super(endpoints, corsConfiguration);
}
4.2.3.设置安全过滤器
MvcEndpointSecurityInterceptor securityInterceptor = new MvcEndpointSecurityInterceptor(
this.managementServerProperties.getSecurity().isEnabled(),
this.managementServerProperties.getSecurity().getRoles());
mapping.setSecurityInterceptor(securityInterceptor);
定义:
/**
* Security interceptor for MvcEndpoints.
*
* @author Madhura Bhave
* @since 1.5.0
*/
public class MvcEndpointSecurityInterceptor extends HandlerInterceptorAdapter { private static final Log logger = LogFactory
.getLog(MvcEndpointSecurityInterceptor.class); private final boolean secure; private final List<String> roles; private AtomicBoolean loggedUnauthorizedAttempt = new AtomicBoolean(); public MvcEndpointSecurityInterceptor(boolean secure, List<String> roles) {
this.secure = secure;
this.roles = roles;
} @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
if (CorsUtils.isPreFlightRequest(request) || !this.secure) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
if (HttpMethod.OPTIONS.matches(request.getMethod())
&& !(handlerMethod.getBean() instanceof MvcEndpoint)) {
return true;
}
MvcEndpoint mvcEndpoint = (MvcEndpoint) handlerMethod.getBean();
if (!mvcEndpoint.isSensitive()) {
return true;
}
if (isUserAllowedAccess(request)) {
return true;
}
sendFailureResponse(request, response);
return false;
} private boolean isUserAllowedAccess(HttpServletRequest request) {
AuthoritiesValidator authoritiesValidator = null;
if (isSpringSecurityAvailable()) {
authoritiesValidator = new AuthoritiesValidator();
}
for (String role : this.roles) {
if (request.isUserInRole(role)) {
return true;
}
if (authoritiesValidator != null && authoritiesValidator.hasAuthority(role)) {
return true;
}
}
return false;
} private boolean isSpringSecurityAvailable() {
return ClassUtils.isPresent(
"org.springframework.security.config.annotation.web.WebSecurityConfigurer",
getClass().getClassLoader());
} private void sendFailureResponse(HttpServletRequest request,
HttpServletResponse response) throws Exception {
if (request.getUserPrincipal() != null) {
String roles = StringUtils.collectionToDelimitedString(this.roles, " ");
response.sendError(HttpStatus.FORBIDDEN.value(),
"Access is denied. User must have one of the these roles: " + roles);
}
else {
logUnauthorizedAttempt();
response.sendError(HttpStatus.UNAUTHORIZED.value(),
"Full authentication is required to access this resource.");
}
} private void logUnauthorizedAttempt() {
if (this.loggedUnauthorizedAttempt.compareAndSet(false, true)
&& logger.isInfoEnabled()) {
logger.info("Full authentication is required to access "
+ "actuator endpoints. Consider adding Spring Security "
+ "or set 'management.security.enabled' to false.");
}
} /**
* Inner class to check authorities using Spring Security (when available).
*/
private static class AuthoritiesValidator { private boolean hasAuthority(String role) {
Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
if (authentication != null) {
for (GrantedAuthority authority : authentication.getAuthorities()) {
if (authority.getAuthority().equals(role)) {
return true;
}
}
}
return false;
}
} }
4.2.4. 自定义EndpointHandlerMapping
@FunctionalInterface
public interface EndpointHandlerMappingCustomizer { /**
* Customize the specified {@link EndpointHandlerMapping}.
* @param mapping the {@link EndpointHandlerMapping} to customize
*/
void customize(EndpointHandlerMapping mapping); }
5.映射的实现EndpointWebMvcManagementContextConfiguration
@Bean
@ConditionalOnBean(EnvironmentEndpoint.class)
@ConditionalOnEnabledEndpoint("env")
public EnvironmentMvcEndpoint environmentMvcEndpoint(EnvironmentEndpoint delegate) {
return new EnvironmentMvcEndpoint(delegate);
} @Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint("heapdump")
public HeapdumpMvcEndpoint heapdumpMvcEndpoint() {
return new HeapdumpMvcEndpoint();
} @Bean
@ConditionalOnBean(HealthEndpoint.class)
@ConditionalOnEnabledEndpoint("health")
public HealthMvcEndpoint healthMvcEndpoint(HealthEndpoint delegate,
ManagementServerProperties managementServerProperties) {
HealthMvcEndpoint healthMvcEndpoint = new HealthMvcEndpoint(delegate,
this.managementServerProperties.getSecurity().isEnabled(),
managementServerProperties.getSecurity().getRoles());
if (this.healthMvcEndpointProperties.getMapping() != null) {
healthMvcEndpoint
.addStatusMapping(this.healthMvcEndpointProperties.getMapping());
}
return healthMvcEndpoint;
} @Bean
@ConditionalOnBean(LoggersEndpoint.class)
@ConditionalOnEnabledEndpoint("loggers")
public LoggersMvcEndpoint loggersMvcEndpoint(LoggersEndpoint delegate) {
return new LoggersMvcEndpoint(delegate);
} @Bean
@ConditionalOnBean(MetricsEndpoint.class)
@ConditionalOnEnabledEndpoint("metrics")
public MetricsMvcEndpoint metricsMvcEndpoint(MetricsEndpoint delegate) {
return new MetricsMvcEndpoint(delegate);
} @Bean
@ConditionalOnEnabledEndpoint("logfile")
@Conditional(LogFileCondition.class)
public LogFileMvcEndpoint logfileMvcEndpoint() {
return new LogFileMvcEndpoint();
} @Bean
@ConditionalOnBean(ShutdownEndpoint.class)
@ConditionalOnEnabledEndpoint(value = "shutdown", enabledByDefault = false)
public ShutdownMvcEndpoint shutdownMvcEndpoint(ShutdownEndpoint delegate) {
return new ShutdownMvcEndpoint(delegate);
} @Bean
@ConditionalOnBean(AuditEventRepository.class)
@ConditionalOnEnabledEndpoint("auditevents")
public AuditEventsMvcEndpoint auditEventMvcEndpoint(
AuditEventRepository auditEventRepository) {
return new AuditEventsMvcEndpoint(auditEventRepository);
}
最终的映射来自MvcEndpoint的各种实现
以health为例:
@ActuatorGetMapping
@ResponseBody
public Object invoke(HttpServletRequest request, Principal principal) {
if (!getDelegate().isEnabled()) {
// Shouldn't happen because the request mapping should not be registered
return getDisabledResponse();
}
Health health = getHealth(request, principal);
HttpStatus status = getStatus(health);
if (status != null) {
return new ResponseEntity<>(health, status);
}
return health;
}
其中,@ActuatorGetMapping注解等同于@RequestMapping
/**
* Specialized {@link RequestMapping} for {@link RequestMethod#GET GET} requests that
* produce {@code application/json} or
* {@code application/vnd.spring-boot.actuator.v1+json} responses.
*
* @author Andy Wilkinson
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.GET, produces = {
ActuatorMediaTypes.APPLICATION_ACTUATOR_V1_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
@interface ActuatorGetMapping { /**
* Alias for {@link RequestMapping#value}.
* @return the value
*/
@AliasFor(annotation = RequestMapping.class)
String[] value() default {}; }
注意,其中涉及到非常重要的一个类:EndpointMvcAdapter,它代理了MvcEndpoint,实现其invoke方法
/**
* Adapter class to expose {@link Endpoint}s as {@link MvcEndpoint}s.
*
* @author Dave Syer
* @author Andy Wilkinson
*/
public class EndpointMvcAdapter extends AbstractEndpointMvcAdapter<Endpoint<?>> { /**
* Create a new {@link EndpointMvcAdapter}.
* @param delegate the underlying {@link Endpoint} to adapt.
*/
public EndpointMvcAdapter(Endpoint<?> delegate) {
super(delegate);
} @Override
@ActuatorGetMapping
@ResponseBody
public Object invoke() {
return super.invoke();
} }
总结:
1.代理层
2.实现层
3.代理逻辑
以HealthMvcEndpoint为例讲述:
HealthMvcEndpoint主方法
@ActuatorGetMapping
@ResponseBody
public Object invoke(HttpServletRequest request, Principal principal) {
if (!getDelegate().isEnabled()) {
// Shouldn't happen because the request mapping should not be registered
return getDisabledResponse();
}
Health health = getHealth(request, principal);
HttpStatus status = getStatus(health);
if (status != null) {
return new ResponseEntity<>(health, status);
}
return health;
}
调用逻辑
private Health getHealth(HttpServletRequest request, Principal principal) {
long accessTime = System.currentTimeMillis();
if (isCacheStale(accessTime)) {
this.lastAccess = accessTime;
this.cached = getDelegate().invoke();
}
if (exposeHealthDetails(request, principal)) {
return this.cached;
}
return Health.status(this.cached.getStatus()).build();
}
delegate获取由HealthMvcEndpoint构造方法注入
public HealthMvcEndpoint(HealthEndpoint delegate, boolean secure,
List<String> roles) {
super(delegate);
this.secure = secure;
setupDefaultStatusMapping();
this.roles = roles;
}
触发HealthEndpoint#invoke()方法:
/**
* Invoke all {@link HealthIndicator} delegates and collect their health information.
*/
@Override
public Health invoke() {
return this.healthIndicator.health();
}
HealthIndicator接口代理了其子类:
spring boot actuator工作原理之http服务暴露源码分析的更多相关文章
- Spring Boot JDBC:加载DataSource过程的源码分析及yml中DataSource的配置
装载至:https://www.cnblogs.com/storml/p/8611388.html Spring Boot实现了自动加载DataSource及相关配置.当然,使用时加上@EnableA ...
- (转)spring boot实战(第三篇)事件监听源码分析
原文:http://blog.csdn.net/liaokailin/article/details/48194777 监听源码分析 首先是我们自定义的main方法: package com.lkl. ...
- 浅尝Spring注解开发_AOP原理及完整过程分析(源码)
浅尝Spring注解开发_AOP原理及完整过程分析(源码) 浅尝Spring注解开发,基于Spring 4.3.12 分析AOP执行过程及源码,包含AOP注解使用.AOP原理.分析Annotation ...
- Spring Cloud Eureka服务注册源码分析
Eureka是怎么work的 那eureka client如何将本地服务的注册信息发送到远端的注册服务器eureka server上.通过下面的源码分析,看出Eureka Client的定时任务调用E ...
- Spring MVC的工作原理,我们来看看其源码实现
前言 开心一刻 晚上陪老丈人吃饭,突然手机响了,我手贱按了免提……哥们:快出来喝酒!哥几个都在呢!我:今天不行,我现在陪老丈人吃饭呢.哥们:那你抓紧喝,我三杯白酒,把我岳父放倒了才出来的,你也快点.看 ...
- 🏆【Alibaba微服务技术系列】「Dubbo3.0技术专题」回顾Dubbo2.x的技术原理和功能实现及源码分析(温故而知新)
RPC服务 什么叫RPC? RPC[Remote Procedure Call]是指远程过程调用,是一种进程间通信方式,他是一种技术的思想,而不是规范.它允许程序调用另一个地址空间(通常是共享网络的另 ...
- Dubbo服务暴露源码解析②
目录 0.配置解析 1.开始export 2.组装URL 3.服务暴露 疑问解析 先放一张官网的服务暴露时序图,对我们梳理源码有很大的帮助.注:不论是暴露还是导出或者是其他翻译,都是描述expor ...
- Spring Cloud Nacos实现动态配置加载的源码分析
理解了上述Environment的基本原理后,如何从远程服务器上加载配置到Spring的Environment中. NacosPropertySourceLocator 顺着前面的分析思路,我们很自然 ...
- 【图灵学院09】RPC底层通讯原理之Netty线程模型源码分析
1. dubbo 2.5.3 netty 3.2.5.Final
随机推荐
- STM32系列ARM单片机介绍
STM32系列基于专为要求高性能.低成本.低功耗的嵌入式应用专门设计的ARM Cortex-M3内核.按性能分成两个不同的系列:STM32F103"增强型"系列和STM32F101 ...
- 一些.NET 项目中经常使用的类库
Web自己主动化測试 Watin Selenium Selenium git .net 集合类扩展实现C5 Subscriber/Publisher 模式 Rx Nats 防御式编程 断言库 流 ...
- 【iOS开发-54】案例学习:通过UIScrollView的缩放图片功能练习代理模式的详细实现
案例:(在模拟器中按住option键,点击鼠标就会出现缩放的手势) (1)在ViewController.m中: --缩放东西是UIScrollView除了滚动之外的还有一个功能,所以须要缩放的东西应 ...
- vuex3
以下是一个表示“单向数据流”理念的极简示意: 但是,当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏: 多个视图依赖于同一状态. 来自不同视图的行为需要变更同一状态. 这就是 Vue ...
- Android 优雅的让Fragment监听返回键
Activity可以很容易的得到物理返回键的监听事件,而Fragment却不能.假设FragmentActivity有三个Fragment,一般安卓用户期望点击返回键会一层层返回到FragmentAc ...
- 如何用一个app操作另外一个app.比如微信群控那样的
如何实现一个app.控制另外的app,比如市面上群控微信的,是用测试工具的原理?还是什么模拟点击的原理? 如何用一个app操作另外一个app.比如微信群控那样的 >> android这个答 ...
- Javascript平稳退化、渐进增强
平稳退化 : javascript平稳退化就是如果一个浏览器完全不支持js或者禁用js的时候,它的基本功能不会受到任何影响.比方说一个网站使用了大量javascript来优化页面,我们现在把浏览器的j ...
- freemarker加载模板文件的
java代码: public String getContent(String name, HashMap<String, Object> paramMap) { //home 文件路径 ...
- eclipse历史版本下载地址
http://wiki.eclipse.org/Older_Versions_Of_Eclipse
- luogu P1365 WJMZBMR打osu! / Easy(期望DP)
题目背景 原 维护队列 参见P1903 题目描述 某一天WJMZBMR在打osu~~~但是他太弱逼了,有些地方完全靠运气:( 我们来简化一下这个游戏的规则 有nnn次点击要做,成功了就是o,失败了就是 ...