前言
本文我们来介绍一下EndpointHandlerMapping. 我们在项目中加入spring-boot-starter-actuator 之后,就可以访问如下端点:
path | 描述 | 是否默认敏感 |
---|---|---|
/shutdown | 优雅关闭 | true |
/dump | 打印出线程的堆栈信息. | true |
/configprops | 显示出所有的@ConfigurationProperties | true |
/info | 显示出任意的应用信息 | false |
/env | 暴露出ConfigurableEnvironment的所有的properties | true |
/health | 显示应用的健康信息 | false |
/mappings | 显示@RequestMapping的所有路径 | true |
/autoconfig | 显示出所有的自动装配的结果 | true |
/metrics | 显示出当前应用的所有metrics的信息 | true |
/trace | 显示出trace的信息(默认只显示100) | true |
/auditevents/td> | 显示出所有的audit events | true |
/heapdump/td> | dump堆内存 | true |
/beans/td> | 展示所有的bean | true |
/loggers/td> | 展示或者修改loggers的配置 | true |
/logfile/td> | 展示logfile的内容 | true |
我们之前解析了一系列的xxxEndpoint,xxxEndpoint是不能直接通过http请求来处理的,因此需要在其上包装一层,因此出现了2个分支–>xxxMvcEndpoint,EndpointMvcAdapter的一系列的子类.但是光这样还不够,我们需要将其注册上去才能在spring mvc 的体系中处理,那么EndpointHandlerMapping就是干这件事的
解析
AbstractEndpointHandlerMapping
AbstractEndpointHandlerMapping 继承自RequestMappingHandlerMapping
字段,构造器如下:
// MVC端点集合 private final Set<E> endpoints; // 安全处理程序拦截器 private HandlerInterceptor securityInterceptor; // CORS配置 private final CorsConfiguration corsConfiguration; // 端点的映射路径前缀 private String prefix = ""; private boolean disabled = false; public AbstractEndpointHandlerMapping(Collection<? extends E> endpoints) { this(endpoints, null); } public AbstractEndpointHandlerMapping(Collection<? extends E> endpoints, CorsConfiguration corsConfiguration) { this.endpoints = new HashSet<E>(endpoints); // 2. 扩展点 postProcessEndpoints(this.endpoints); this.corsConfiguration = corsConfiguration; // By default the static resource handler mapping is LOWEST_PRECEDENCE - 1 // and the RequestMappingHandlerMapping is 0 (we ideally want to be before both) // 默认情况下,静态资源处理程序映射的顺序是 LOWEST_PRECEDENCE - 1 setOrder(-100); // 不进行后缀匹配 setUseSuffixPatternMatch(false); }
在构造器中调用了postProcessEndpoints,默认空实现,代码如下:
protected void postProcessEndpoints(Set<E> endpoints) { }
覆写了如下方法:
afterPropertiesSet,代码如下:
public void afterPropertiesSet() { super.afterPropertiesSet(); if (!this.disabled) { // 如果端点是启用的 for (MvcEndpoint endpoint : this.endpoints) { // 调用AbstractHandlerMethodMapping中的detectHandlerMethods方法进行注册handler,寻找在MvcEndpoint有@RequestMapping注解的方法 detectHandlerMethods(endpoint); } } }
- 调用父类的afterPropertiesSet
- 如果disabled等于true,则遍历endpoints,依次调用detectHandlerMethods,进行注册handler,寻找在MvcEndpoint有@RequestMapping注解的方法.此时就将xxxMvcEndpoint,EndpointMvcAdapter的一系列的子类被@ActuatorGetMapping注解的方法进行了注册
isHandler–>因为所有的handler都已在afterPropertiesSet中进行了处理,因此这里就不需要判断了,直接返回false.如下:
protected boolean isHandler(Class<?> beanType) { return false; }
registerHandlerMethod–> 复写了AbstractHandlerMethodMapping#detectHandlerMethods.修改了handler的pattern.代码如下:
protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) { if (mapping == null) { return; } String[] patterns = getPatterns(handler, mapping); if (!ObjectUtils.isEmpty(patterns)) { // 重新注册了映射规则 super.registerHandlerMethod(handler, method, withNewPatterns(mapping, patterns)); } }
- 如果RequestMappingInfo等于null,则直接return
获得对应的pattern.代码如下:
private String[] getPatterns(Object handler, RequestMappingInfo mapping) { if (handler instanceof String) { handler = getApplicationContext().getBean((String) handler); } Assert.state(handler instanceof MvcEndpoint, "Only MvcEndpoints are supported"); String path = getPath((MvcEndpoint) handler); // 获得MvcEndpoint配置的path return (path == null ? null : getEndpointPatterns(path, mapping)); }
- 如果handler是String的实例,则将其看做bean id 获得对应的handler
获得MvcEndpoint配置的path.代码如下:
protected String getPath(MvcEndpoint endpoint) { return endpoint.getPath(); }
前缀处理.代码如下:
private String[] getEndpointPatterns(String path, RequestMappingInfo mapping) { // 1. 路径模式前缀 String patternPrefix = StringUtils.hasText(this.prefix) ? this.prefix + path : path; // 2. 根据RequestMappingInfo 获得匹配的路径 Set<String> defaultPatterns = mapping.getPatternsCondition().getPatterns(); // 3. 如果defaultPatterns为空 if (defaultPatterns.isEmpty()) { return new String[] { patternPrefix, patternPrefix + ".json" }; } // 4. 如果不为空,则加defaultPatterns的路径前加上前缀 List<String> patterns = new ArrayList<String>(defaultPatterns); for (int i = 0; i < patterns.size(); i++) { patterns.set(i, patternPrefix + patterns.get(i)); } return patterns.toArray(new String[patterns.size()]); }
- 路径模式前缀
- 根据RequestMappingInfo 获得匹配的路径
- 如果defaultPatterns为空,则直接返回patternPrefix,patternPrefix.json
- 如果不为空,则加defaultPatterns的路径前加上前缀
拿MetricsMvcEndpoint来说,其继承自EndpointMvcAdapter,内部持有的是MetricsEndpoint.由于默认情况下,我们没有management.context-path,因此在第1步返回的是/metrics
由于在MetricsMvcEndpoint中有两个被@ActuatorGetMapping注解的方法:
声明在EndpointMvcAdapter中的invoke方法,如下:
@Override @ActuatorGetMapping @ResponseBody public Object invoke() { return super.invoke(); }
由于没有配置@ActuatorGetMapping中的value属性,因此,也就是在第2步中没有获取到defaultPatterns,因此,最终在第3步返回/metrics,/metrics.json 然后进行注册.
value方法,声明在MetricsMvcEndpoint中,代码如下:
@ActuatorGetMapping("/{name:.*}") @ResponseBody @HypermediaDisabled public Object value(@PathVariable String name) { .... }
由于配置@ActuatorGetMapping中的value属性–>/{name:.*},因此,在第2步中获取到defaultPatterns–>/{name:.*},因此,最终在第4步返回/metrics/{name:.*}, 然后进行注册.
因此我们在启动应用时,会看到如下日志:
2018-02-01 15:24:52.984 INFO 7106 --- [ 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) 2018-02-01 15:24:52.984 INFO 7106 --- [ 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()
重新注册映射规则–>在注册hanler时重新修改了RequestMappingInfo的mapping.代码如下:
private RequestMappingInfo withNewPatterns(RequestMappingInfo mapping, String[] patternStrings) { PatternsRequestCondition patterns = new PatternsRequestCondition(patternStrings, null, null, useSuffixPatternMatch(), useTrailingSlashMatch(), null); return new RequestMappingInfo(patterns, mapping.getMethodsCondition(), mapping.getParamsCondition(), mapping.getHeadersCondition(), mapping.getConsumesCondition(), mapping.getProducesCondition(), mapping.getCustomCondition()); }
getHandlerExecutionChain–>获取处理程序执行链,在DispatcherServlet#doDispatch中会调用HandlerMapping#getHandler,而在getHandler中会调用该方法来获得HandlerExecutionChain,HandlerExecutionChain是由一系列的拦截器+handler组成的.代码如下:
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { // 1. 调用父类的处理 HandlerExecutionChain chain = super.getHandlerExecutionChain(handler, request); // 2. 如果securityInterceptor 等于null或者是CORS请求,则直接返回 if (this.securityInterceptor == null || CorsUtils.isCorsRequest(request)) { return chain; } // 3. 在原先的Interceptor基础上加上securityInterceptor return addSecurityInterceptor(chain); }
- 调用父类的处理
- 如果securityInterceptor 等于null或者是CORS请求,则直接返回
在原先的Interceptor基础上加上securityInterceptor.代码如下:
private HandlerExecutionChain addSecurityInterceptor(HandlerExecutionChain chain) { List<HandlerInterceptor> interceptors = new ArrayList<HandlerInterceptor>(); if (chain.getInterceptors() != null) { interceptors.addAll(Arrays.asList(chain.getInterceptors())); } interceptors.add(this.securityInterceptor); return new HandlerExecutionChain(chain.getHandler(), interceptors.toArray(new HandlerInterceptor[interceptors.size()])); }
关于securityInterceptor我们后面有讲解
extendInterceptors –> 该方法是在AbstractHandlerMapping#initApplicationContext中调用的,调用的时间是在该HandlerMapping初始化时调用的.代码如下:
protected void extendInterceptors(List<Object> interceptors) { interceptors.add(new SkipPathExtensionContentNegotiation()); }
SkipPathExtensionContentNegotiation,在前置处理中向request中保存了org.springframework.web.accept.PathExtensionContentNegotiationStrategy.SKIP的属性,值为true.代码如下:
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { request.setAttribute(SKIP_ATTRIBUTE, Boolean.TRUE); return true; }
initCorsConfiguration–>复写了RequestMappingHandlerMapping#initCorsConfiguration,直接使用本类配置的corsConfiguration,该方法在注册Handler时有用.代码如下:
protected CorsConfiguration initCorsConfiguration(Object handler, Method method, RequestMappingInfo mappingInfo) { return this.corsConfiguration; }
MvcEndpointSecurityInterceptor
AbstractEndpointHandlerMapping中持有的securityInterceptor默认是MvcEndpointSecurityInterceptor(自动装配).
其字段,构造器如下:
private static final Log logger = LogFactory .getLog(MvcEndpointSecurityInterceptor.class); // 是否进行校验 private final boolean secure; // 默认为ACTUATOR private final List<String> roles; private AtomicBoolean loggedUnauthorizedAttempt = new AtomicBoolean(); public MvcEndpointSecurityInterceptor(boolean secure, List<String> roles) { this.secure = secure; this.roles = roles; }
preHandle方法如下:
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 1. 如果是(cors请求并且是options类型的请求并且请求头Access-Control-Request-Method存在)或者(不进行校验),则直接返回true if (CorsUtils.isPreFlightRequest(request) || !this.secure) { return true; } HandlerMethod handlerMethod = (HandlerMethod) handler; // 2. 如果是options请求并且HandlerMethod不是MvcEndpoint的实例,则返回true if (HttpMethod.OPTIONS.matches(request.getMethod()) && !(handlerMethod.getBean() instanceof MvcEndpoint)) { return true; } MvcEndpoint mvcEndpoint = (MvcEndpoint) handlerMethod.getBean(); // 3. 如果配置的endpoints.sensitive=false或者对应的endpoints.xxx.sensitive=false,则直接返回true if (!mvcEndpoint.isSensitive()) { return true; } // 4. 如果拥有相应的权限则返回true if (isUserAllowedAccess(request)) { return true; } // 5. 返回401 sendFailureResponse(request, response); return false; }
- 如果是(cors请求并且是options类型的请求并且请求头Access-Control-Request-Method存在)或者(不进行校验),则直接返回true
- 如果是options请求并且HandlerMethod不是MvcEndpoint的实例,则返回true
- 如果配置的endpoints.sensitive=false或者对应的endpoints.xxx.sensitive=false,则直接返回true
如果拥有相应的权限则返回true.代码如下:
private boolean isUserAllowedAccess(HttpServletRequest request) { AuthoritiesValidator authoritiesValidator = null; // 1. 如果org.springframework.security.config.annotation.web.WebSecurityConfigurer在当前类路径下存在,也就是加入了spring-boot-starter-security的依赖 // 则实例化AuthoritiesValidator if (isSpringSecurityAvailable()) { authoritiesValidator = new AuthoritiesValidator(); } // 2. 遍历需要的权限,依次判断其是否有对应的权限,只要有1个满足的话,则返回true for (String role : this.roles) { // 2.1 判断用户是否是某个种类的角色,类型可以在web.xml中配置(tomcat-users.xml) if (request.isUserInRole(role)) { return true; } // 2.2 如果authoritiesValidator不等于null并且拥有对应的权限,则返回true if (authoritiesValidator != null && authoritiesValidator.hasAuthority(role)) { return true; } } // 3. 如果都不满足,返回false return false; }
- 如果org.springframework.security.config.annotation.web.WebSecurityConfigurer在当前类路径下存在,也就是加入了spring-boot-starter-security的依赖,则实例化AuthoritiesValidator
遍历需要的权限,依次判断其是否有对应的权限,只要有1个满足的话,则返回true
- 判断用户是否是某个种类的角色,类型可以在web.xml中配置(tomcat-users.xml)
如果authoritiesValidator不等于null并且拥有对应的权限,则返回true.代码如下:
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; }
如果都不满足,返回false
- 返回401
EndpointHandlerMapping
EndpointHandlerMapping 继承自AbstractEndpointHandlerMapping.代码如下:
public class EndpointHandlerMapping extends AbstractEndpointHandlerMapping<MvcEndpoint> {
public EndpointHandlerMapping(Collection<? extends MvcEndpoint> endpoints) {
super(endpoints);
}
// 创建一个EndpointHandlerMapping.
public EndpointHandlerMapping(Collection<? extends MvcEndpoint> endpoints,
CorsConfiguration corsConfiguration) {
super(endpoints, corsConfiguration);
}
}
自动装配
EndpointHandlerMapping的自动装配是在EndpointWebMvcAutoConfiguration中.
EndpointWebMvcAutoConfiguration声明了如下注解,在满足如下条件时生效:
@Configuration @ConditionalOnClass({ Servlet.class, DispatcherServlet.class }) @ConditionalOnWebApplication @AutoConfigureAfter({ PropertyPlaceholderAutoConfiguration.class, EmbeddedServletContainerAutoConfiguration.class, WebMvcAutoConfiguration.class, ManagementServerPropertiesAutoConfiguration.class, RepositoryRestMvcAutoConfiguration.class, HypermediaAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class })
- @ConditionalOnClass({ Servlet.class, DispatcherServlet.class }) –> 在类路径下存在Servlet.class, DispatcherServlet.class时生效
- @ConditionalOnWebApplication –> 是web环境时生效
EndpointHandlerMapping实现了ApplicationContextAware, BeanFactoryAware, SmartInitializingSingleton接口,其中,当EndpointHandlerMapping初始化之后会回调afterSingletonsInstantiated.代码如下:
public void afterSingletonsInstantiated() { // 1. 判断 managementPort 是否和serverPort一样 ManagementServerPort managementPort = ManagementServerPort.DIFFERENT; if (this.applicationContext instanceof WebApplicationContext) { managementPort = ManagementServerPort .get(this.applicationContext.getEnvironment(), this.beanFactory); } // 2. 如果不一样 if (managementPort == ManagementServerPort.DIFFERENT) { // 2.1 如果当前applicationContext为EmbeddedWebApplicationContext,并且有嵌入容器,则创建一个子WebApplicationContext // note: 这也意味着使用actuate时,如果managementPort 和serverPort 不一样时,并且是部署在外置tomcat中时,actuate 会不生效的 if (this.applicationContext instanceof EmbeddedWebApplicationContext && ((EmbeddedWebApplicationContext) this.applicationContext) .getEmbeddedServletContainer() != null) { createChildManagementContext(); } else { // 否则,不进行创建 logger.warn("Could not start embedded management container on " + "different port (management endpoints are still available " + "through JMX)"); } } // 3. 如果一样,则 if (managementPort == ManagementServerPort.SAME) { // 3.1 如果management.ssl.enabled 配置为true,则抛出IllegalStateException if (new RelaxedPropertyResolver(this.applicationContext.getEnvironment(), "management.ssl.").getProperty("enabled", Boolean.class, false)) { throw new IllegalStateException( "Management-specific SSL cannot be configured as the management " + "server is not listening on a separate port"); } // 3.2 向environment中添加名为Management Server的PropertySource,其中获得local.management.port时,会转而向environment调用local.server.port // 访问其他的属性直接返回null if (this.applicationContext .getEnvironment() instanceof ConfigurableEnvironment) { addLocalManagementPortPropertyAlias( (ConfigurableEnvironment) this.applicationContext .getEnvironment()); } } }
判断 managementPort 是否和serverPort一样.代码如下:
public static ManagementServerPort get(Environment environment, BeanFactory beanFactory) { // 1. 获得server.port的配置 Integer serverPort = getPortProperty(environment, "server."); // 2. 如果server.port没有配置并且ServerProperties在beanFactory中存在的话 if (serverPort == null && hasCustomBeanDefinition(beanFactory, ServerProperties.class, ServerPropertiesAutoConfiguration.class)) { // 则获得ServerProperties中所配置的默认端口 serverPort = getTemporaryBean(beanFactory, ServerProperties.class) .getPort(); } // 3. 获得management.port的配置,如果management.port没有配置并且ManagementServerProperties在beanFactory中存在的话 // 则获得ServerProperties中所配置的默认端口 Integer managementPort = getPortProperty(environment, "management."); if (managementPort == null && hasCustomBeanDefinition(beanFactory, ManagementServerProperties.class, ManagementServerPropertiesAutoConfiguration.class)) { managementPort = getTemporaryBean(beanFactory, ManagementServerProperties.class).getPort(); } // 4. 如果managementPort端口号小于零则返回不可用 if (managementPort != null && managementPort < 0) { return DISABLE; } /* * 5. 如果 * <ul> * <li>managementPort等于null(management.port没有配置)</li> * <li>managementPort 等于8080</li> * <li>managementPort 等于serverPort</li> * </ul> * 满足其中一个,则 managementPort 使用和serverPort一样的端口,返回same,否则,返回DIFFERENT * */ return ((managementPort == null) || (serverPort == null && managementPort.equals(8080)) || (managementPort != 0 && managementPort.equals(serverPort)) ? SAME : DIFFERENT); }
- 获得server.port的配置
- 如果server.port没有配置并且ServerProperties在beanFactory中存在的话,则获得ServerProperties中所配置的默认端口
- 获得management.port的配置,如果management.port没有配置并且ManagementServerProperties在beanFactory中存在的话,则获得ServerProperties中所配置的默认端口
- 如果managementPort端口号小于零则返回不可用
如果满足以下条件的任意1个,则managementPort 使用和serverPort一样的端口,返回same,否则,返回DIFFERENT:
- managementPort等于null(management.port没有配置)
- managementPort 等于8080
- managementPort 等于serverPort
如果不一样
如果当前applicationContext为EmbeddedWebApplicationContext,并且有嵌入容器,则创建一个子WebApplicationContext
这也意味着使用actuate时,如果managementPort 和serverPort 不一样时,并且是部署在外置tomcat中时,actuate 会不生效的
如果一样,则
- 如果management.ssl.enabled 配置为true,则抛出IllegalStateException
向environment中添加名为Management Server的PropertySource,其中获得local.management.port时,会转而向environment调用local.server.port.访问其他的属性直接返回null.代码如下:
private void addLocalManagementPortPropertyAlias( final ConfigurableEnvironment environment) { environment.getPropertySources() .addLast(new PropertySource<Object>("Management Server") { @Override public Object getProperty(String name) { if ("local.management.port".equals(name)) { return environment.getProperty("local.server.port"); } return null; } }); }
其中:当 managementPort 和serverPort 不一样,会执行如下代码:
private void createChildManagementContext() { AnnotationConfigEmbeddedWebApplicationContext childContext = new AnnotationConfigEmbeddedWebApplicationContext(); childContext.setParent(this.applicationContext); childContext.setNamespace("management"); childContext.setId(this.applicationContext.getId() + ":management"); childContext.setClassLoader(this.applicationContext.getClassLoader()); // 1. 注册配置类 childContext.register(EndpointWebMvcChildContextConfiguration.class, PropertyPlaceholderAutoConfiguration.class, EmbeddedServletContainerAutoConfiguration.class, DispatcherServletAutoConfiguration.class); // 2. 注册嵌入容器Factory registerEmbeddedServletContainerFactory(childContext); // 3. 添加CloseManagementContextListener监听器--> 处理ContextClosedEvent,ApplicationFailedEvent 事件 CloseManagementContextListener.addIfPossible(this.applicationContext, childContext); // 4. 进行初始化 childContext.refresh(); // 5. 为ManagementContextResolver 重新设置ApplicationContext 为创建的子WebApplicationContext managementContextResolver().setApplicationContext(childContext); }
- 实例化AnnotationConfigEmbeddedWebApplicationContext
- 注册配置类
注册嵌入容器Factory.代码如下:
private void registerEmbeddedServletContainerFactory( AnnotationConfigEmbeddedWebApplicationContext childContext) { try { ConfigurableListableBeanFactory beanFactory = childContext.getBeanFactory(); if (beanFactory instanceof BeanDefinitionRegistry) { BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; registry.registerBeanDefinition("embeddedServletContainerFactory", new RootBeanDefinition( determineEmbeddedServletContainerFactoryClass())); } } catch (NoSuchBeanDefinitionException ex) { // Ignore and assume auto-configuration } }
调用:
private Class<?> determineEmbeddedServletContainerFactoryClass() throws NoSuchBeanDefinitionException { Class<?> servletContainerFactoryClass = this.applicationContext .getBean(EmbeddedServletContainerFactory.class).getClass(); if (cannotBeInstantiated(servletContainerFactoryClass)) { throw new FatalBeanException("EmbeddedServletContainerFactory implementation " + servletContainerFactoryClass.getName() + " cannot be instantiated. " + "To allow a separate management port to be used, a top-level class " + "or static inner class should be used instead"); } return servletContainerFactoryClass; }
添加CloseManagementContextListener监听器–> 处理ContextClosedEvent,ApplicationFailedEvent 事件.其最终处理都是关闭当前上下文.如下:
private void propagateCloseIfNecessary(ApplicationContext applicationContext) { if (applicationContext == this.parentContext) { this.childContext.close(); } }
- 进行初始化
- 为ManagementContextResolver 重新设置ApplicationContext 为创建的子WebApplicationContext
EndpointWebMvcAutoConfiguration中有2个配置内部类:
ApplicationContextFilterConfiguration,代码如下:
@Configuration @ConditionalOnProperty(prefix = "management", name = "add-application-context-header", matchIfMissing = true, havingValue = "true") protected static class ApplicationContextFilterConfiguration { // ApplicationContextHeaderFilter是添加名为X-Application-Context的响应头,值为ApplicationContext的id @Bean public ApplicationContextHeaderFilter applicationContextIdFilter( ApplicationContext context) { return new ApplicationContextHeaderFilter(context); } }
当配置有management.add-application-context-header = true或者没有配置时默认生效.注册1个ApplicationContextHeaderFilter.其作用是添加名为X-Application-Context的响应头,值为ApplicationContext的id.代码如下:
public static final String HEADER_NAME = "X-Application-Context"; private final ApplicationContext applicationContext; public ApplicationContextHeaderFilter(ApplicationContext context) { this.applicationContext = context; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { response.addHeader(HEADER_NAME, this.applicationContext.getId()); filterChain.doFilter(request, response); }
EndpointWebMvcConfiguration,代码如下:
@Configuration @Conditional(OnManagementMvcCondition.class) @Import(ManagementContextConfigurationsImportSelector.class) protected static class EndpointWebMvcConfiguration { }
当满足如下条件的任意1个时生效:
- managementPort等于null(management.port没有配置)
- managementPort 等于8080
- managementPort 等于serverPort
通过@Import导入了ManagementContextConfigurationsImportSelector.由于其是DeferredImportSelector的实例,因此会调用其selectImports方法,如下:
public String[] selectImports(AnnotationMetadata metadata) { // Find all management context configuration classes, filtering duplicates // 1. 加载/META-INF/spring.factories中配置的org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration List<ManagementConfiguration> configurations = getConfigurations(); // 2. 排序 OrderComparator.sort(configurations); List<String> names = new ArrayList<String>(); for (ManagementConfiguration configuration : configurations) { names.add(configuration.getClassName()); } return names.toArray(new String[names.size()]); }
- 加载/META-INF/spring.factories中配置的org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration
- 排序
默认返回的是:
org.springframework.boot.actuate.autoconfigure.EndpointWebMvcManagementContextConfiguration,\ org.springframework.boot.actuate.autoconfigure.EndpointWebMvcHypermediaManagementContextConfiguration
EndpointWebMvcHypermediaManagementContextConfiguration,其注解如下:
@ManagementContextConfiguration @ConditionalOnClass(Link.class) @ConditionalOnWebApplication @ConditionalOnBean(HttpMessageConverters.class) @Conditional(EndpointHypermediaEnabledCondition.class) @EnableConfigurationProperties(ResourceProperties.class)
由于默认情况下,在类路径下不存在org.springframework.hateoas.Link.class,因此该类不会进行处理.
EndpointWebMvcManagementContextConfiguration,在这个配置类中,除了配置了一系列的mvcEndpoints之外,还配置了endpointHandlerMapping.如下:
@Bean @ConditionalOnMissingBean public EndpointHandlerMapping endpointHandlerMapping() { // 1. 获得注册的MvcEndpoint Set<MvcEndpoint> endpoints = mvcEndpoints().getEndpoints(); // 2. 实例化CorsConfiguration CorsConfiguration corsConfiguration = getCorsConfiguration(this.corsProperties); // 3. 实例化EndpointHandlerMapping-->actuate 系列的HandlerMapping EndpointHandlerMapping mapping = new EndpointHandlerMapping(endpoints, corsConfiguration); // 4. 设置EndpointHandlerMapping的前缀为management.contextPath 的配置值 mapping.setPrefix(this.managementServerProperties.getContextPath()); // 5. 实例化MvcEndpointSecurityInterceptor--> Interceptor,并进行设置 MvcEndpointSecurityInterceptor securityInterceptor = new MvcEndpointSecurityInterceptor( this.managementServerProperties.getSecurity().isEnabled(), this.managementServerProperties.getSecurity().getRoles()); mapping.setSecurityInterceptor(securityInterceptor); // 6. 个性化设置--> 默认没有实现类 for (EndpointHandlerMappingCustomizer customizer : this.mappingCustomizers) { customizer.customize(mapping); } return mapping; }
- 获得注册的MvcEndpoint
- 实例化CorsConfiguration
- 实例化EndpointHandlerMapping–>actuate 系列的HandlerMapping
- 设置EndpointHandlerMapping的前缀为management.contextPath 的配置值
- 实例化MvcEndpointSecurityInterceptor–> Interceptor,并进行设置
- 个性化设置–> 默认没有实现类
此刻就将EndpointHandlerMapping注册了,当DispatcherServlet第1次实例化或者当BeanFactory发送ContextRefreshedEvent事件时,就会调用DispatcherServlet的initHandlerMappings方法,在该方法中,将BeanFactory中所有的HandlerMapping的bean都加入到了handlerMappings中.代码如下:
private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; if (this.detectAllHandlerMappings) { // Find all HandlerMappings in the ApplicationContext, including ancestor contexts. Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values()); // We keep HandlerMappings in sorted order. AnnotationAwareOrderComparator.sort(this.handlerMappings); } } else { try { HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); this.handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { // Ignore, we'll add a default HandlerMapping later. } } // Ensure we have at least one HandlerMapping, by registering // a default HandlerMapping if no other mappings are found. if (this.handlerMappings == null) { this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isDebugEnabled()) { logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default"); } } }
调用链如下: