1. 程式人生 > >Spring Boot之執行器端點(Actuator Endpoint)實現剖析

Spring Boot之執行器端點(Actuator Endpoint)實現剖析

整體實現思路是將端點(Endpoint)適配委託給MVC層策略端點(MvcEndpoint),再通過端點MVC介面卡(EndpointMvcAdapter)將端點暴露為HTTP請求方式的MVC端點,最後分別使用端點自動配置(EndpointAutoConfiguration)和MVC方式暴露端點的配置(EndpointWebMvcManagementContextConfiguration)來注入端點元件和端點處理程式對映元件、MVC端點登錄檔元件、MVC端點元件。

其中,端點處理程式對映(EndpointHandlerMapping)通過Spring MVC方式來暴露MVC端點。最後,本文以“shutdown端點示例”收尾。

現在就按照整體實現思路來剖析HTTP端點的實現原理。

1、端點介面(Endpoint<T>)
/**
* An endpoint that can be used to expose useful information to operations. Usually
* exposed via Spring MVC but could also be exposed using some other technique. Consider
* extending {@link AbstractEndpoint} if you are developing your own endpoint.
* <p>一個端點可以用於暴露操作的實用資訊。
*
* @param <T> the endpoint data type (端點資料型別)
* @see AbstractEndpoint
*/
// 核心介面 端點介面
public interface Endpoint<T> {

/**
* 端點的邏輯標識(字母、數字和下劃線('_'))
*/
String getId();

/**
* 端點是否啟用
*/
boolean isEnabled();

/**
* 端點是否輸出敏感資料(安全提示)
*/
boolean isSensitive();

// 核心介面 呼叫端點,並返回呼叫結果
T invoke();

}

其抽象實現基類 AbstractEndpoint<T>
/**
* Abstract base for {@link Endpoint} implementations.
*
* @param <T> the endpoint data type (端點資料型別)
*/
// 核心類 端點實現的抽象基類
public abstract class AbstractEndpoint<T> implements Endpoint<T>, EnvironmentAware {

private Environment environment;

/**
* Endpoint identifier. With HTTP monitoring the identifier of the endpoint is mapped
* to a URL (e.g. 'foo' is mapped to '/foo').
* 端點識別符號
*/
private String id;

/**
* Mark if the endpoint exposes sensitive information.
*/
private Boolean sensitive;

/**
* 是否啟動端點
*/
private Boolean enabled;


public AbstractEndpoint(String id, boolean sensitive, boolean enabled) {
setId(id);
this.sensitiveDefault = sensitive;
this.enabled = enabled;
}

@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}

public void setId(String id) {
Assert.notNull(id, "Id must not be null");
Assert.isTrue(ID_PATTERN.matcher(id).matches(),
"Id must only contains letters, numbers and '_'");
this.id = id;
}

@Override
public boolean isEnabled() {
return EndpointProperties.isEnabled(this.environment, this.enabled);
}

}

2、MVC層策略端點(MvcEndpoint)
/**
* 實現類允許使用@RequestMapping和完整的Spring MVC機制,
* 但不能在型別級別使用@Controller或@RequestMapping,因為這將導致路徑的雙重對映,
* 一次通過常規MVC處理程式對映,一次通過{@link EndpointHandlerMapping}。
*
* @author Dave Syer
* @see NamedMvcEndpoint
*/
// 核心介面 在端點之上的MVC層策略
public interface MvcEndpoint {

/**
* 禁用端點的響應實體
*/
ResponseEntity<Map<String, String>> DISABLED_RESPONSE = new ResponseEntity<>(
Collections.singletonMap("message", "This endpoint is disabled"),
HttpStatus.NOT_FOUND);

// 核心方法 返回端點的MVC路徑
String getPath();

/**
* 返回端點是否暴露敏感資訊。
*/
boolean isSensitive();

// 核心方法 返回端點暴露的型別/null
@SuppressWarnings("rawtypes")
Class<? extends Endpoint> getEndpointType();

}

2.1、包括邏輯名稱的MVC端點(NamedMvcEndpoint)
/**
* 名稱提供了引用端點的一致方式。
*
* @author Madhura Bhave
* @since 1.5.0
*/
// 包括邏輯名稱的MVC端點
public interface NamedMvcEndpoint extends MvcEndpoint {

/**
* 返回端點的邏輯名稱。
*/
String getName();

}

3、端點MVC介面卡(EndpointMvcAdapter)
/**
* 暴露端點({@link Endpoint})為MVC端點({@link MvcEndpoint})的介面卡。
*/
// 端點MVC介面卡
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); // 委託代理
}


// 核心實現 以HTTP GET方式呼叫
@Override
@ActuatorGetMapping
@ResponseBody
public Object invoke() {
return super.invoke(); // 向上呼叫,鏈式模式
}

}

其抽象實現基類 AbstractEndpointMvcAdapter<E extends Endpoint<?>>

/**
* MVC端點({@link MvcEndpoint})實現的抽象基類。
*
* @param <E> The delegate endpoint (代理的端點)
* @author Dave Syer
* @since 1.3.0
*/
public abstract class AbstractEndpointMvcAdapter<E extends Endpoint<?>>
implements NamedMvcEndpoint {

/**
* 被代理的底層端點(端點子類)
*/
private final E delegate;

/**
* 端點URL路徑
*/
private String path;


public AbstractEndpointMvcAdapter(E delegate) {
Assert.notNull(delegate, "Delegate must not be null");
this.delegate = delegate;
}

// 核心實現 呼叫底層端點,並返回呼叫結果
protected Object invoke() {
if (!this.delegate.isEnabled()) { // 端點被禁用
// Shouldn't happen - shouldn't be registered when delegate's disabled
return getDisabledResponse();
}
return this.delegate.invoke(); // 呼叫端點
}

public E getDelegate() {
return this.delegate;
}

@Override
public String getName() {
return this.delegate.getId(); // name = id
}

@Override
public String getPath() {
return (this.path != null ? this.path : "/" + this.delegate.getId()); // "/id"
}

public void setPath(String path) {
while (path.endsWith("/")) {
path = path.substring(0, path.length() - 1);
}
if (!path.startsWith("/")) {
path = "/" + path;
}
this.path = path;
}

@Override
@SuppressWarnings("rawtypes")
public Class<? extends Endpoint> getEndpointType() {
return this.delegate.getClass();
}

}

4、端點元件自動配置
基於Spring Boot的自動配置機制(Auto-configuration),其自動配置檔案位於spring-boot-actuator資源目錄下的META-INF/spring.factories檔案:

# 啟用自動配置
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
...
org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration,\
...

# 管理上下文配置
org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration=\
org.springframework.boot.actuate.autoconfigure.EndpointWebMvcManagementContextConfiguration,\
...

4.1、公共管理的端點自動配置(EndpointAutoConfiguration)
/**
* {@link EnableAutoConfiguration Auto-configuration} for common management
* {@link Endpoint}s.
*/
// 核心類 公共管理的端點自動配置
@Configuration // 元件配置
@AutoConfigureAfter({ FlywayAutoConfiguration.class, LiquibaseAutoConfiguration.class })
@EnableConfigurationProperties(EndpointProperties.class) // 啟用配置屬性(端點屬性)
public class EndpointAutoConfiguration {

@Bean
@ConditionalOnMissingBean
public EnvironmentEndpoint environmentEndpoint() {
return new EnvironmentEndpoint();
}

@Bean
@ConditionalOnMissingBean
public HealthEndpoint healthEndpoint() {
return new HealthEndpoint(
this.healthAggregator == null ? new OrderedHealthAggregator()
: this.healthAggregator,
this.healthIndicators == null
? Collections.<String, HealthIndicator>emptyMap()
: this.healthIndicators);
}

@Bean
@ConditionalOnMissingBean
public TraceEndpoint traceEndpoint() {
return new TraceEndpoint(this.traceRepository == null
? new InMemoryTraceRepository() : this.traceRepository);
}

@Bean
@ConditionalOnBean(ConditionEvaluationReport.class)
@ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
public AutoConfigurationReportEndpoint autoConfigurationReportEndpoint() {
return new AutoConfigurationReportEndpoint();
}

@Bean
@ConditionalOnMissingBean
public ShutdownEndpoint shutdownEndpoint() {
return new ShutdownEndpoint();
}

}

4.2、全域性的端點屬性(EndpointProperties)
/**
* Global endpoint properties.
* <p>全域性的端點屬性。
*
* @since 1.3.0
*/
@ConfigurationProperties(prefix = "endpoints") // 端點屬性配置字首
public class EndpointProperties {

private static final String ENDPOINTS_ENABLED_PROPERTY = "endpoints.enabled";

private static final String ENDPOINTS_SENSITIVE_PROPERTY = "endpoints.sensitive";

/**
* Enable endpoints.
* 啟用端點
*/
private Boolean enabled = true;

/**
* Default endpoint sensitive setting.
*/
private Boolean sensitive;


public static boolean isEnabled(Environment environment, Boolean enabled) {
if (enabled != null) {
return enabled;
}
if (environment != null
&& environment.containsProperty(ENDPOINTS_ENABLED_PROPERTY)) {
return environment.getProperty(ENDPOINTS_ENABLED_PROPERTY, Boolean.class);
}
return true;
}

}

4.3、外部化配置的註解(ConfigurationProperties)
/**
* 如果要繫結和驗證一些外部屬性(例如來自.properties檔案),請將其新增到@Configuration類中的類定義或@Bean方法。
* <p>
* Note that contrary to {@code @Value}, SpEL expressions are not evaluated since property
* values are externalized.
*
* @author Dave Syer
* @see ConfigurationPropertiesBindingPostProcessor
* @see EnableConfigurationProperties
*/
// 外部化配置的註解
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ConfigurationProperties {

// 屬性的名稱字首
@AliasFor("value")
String prefix() default "";

}

5、MVC方式暴露端點的配置(EndpointWebMvcManagementContextConfiguration)
/**
* Configuration to expose {@link Endpoint} instances over Spring MVC.
*
* @author Dave Syer
* @since 1.3.0
*/
// 核心類 通過MVC方式來暴露端點的配置
@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;


// 核心方法 注入端點處理程式對映元件
@Bean
@ConditionalOnMissingBean // 元件未注入
public EndpointHandlerMapping endpointHandlerMapping() {
// 註冊的MVC端點集合
Set<MvcEndpoint> endpoints = mvcEndpoints().getEndpoints();
CorsConfiguration corsConfiguration = getCorsConfiguration(this.corsProperties);
// 端點處理程式對映
EndpointHandlerMapping mapping = new EndpointHandlerMapping(endpoints,
corsConfiguration);
// 管理端點的上下文路徑字首
mapping.setPrefix(this.managementServerProperties.getContextPath());
// MVC端點安全處理程式攔截器
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;
}

// 核心方法 注入MVC端點登錄檔元件
@Bean
@ConditionalOnMissingBean // 元件未注入
public MvcEndpoints mvcEndpoints() {
return new MvcEndpoints();
}

@Bean
@ConditionalOnBean(EnvironmentEndpoint.class)
@ConditionalOnEnabledEndpoint("env")
public EnvironmentMvcEndpoint environmentMvcEndpoint(EnvironmentEndpoint delegate) {
return new EnvironmentMvcEndpoint(delegate);
}

@Bean
@ConditionalOnBean(HealthEndpoint.class)
@ConditionalOnEnabledEndpoint("health")
public HealthMvcEndpoint healthMvcEndpoint(HealthEndpoint delegate) {
HealthMvcEndpoint healthMvcEndpoint = new HealthMvcEndpoint(delegate,
this.managementServerProperties.getSecurity().isEnabled());
if (this.healthMvcEndpointProperties.getMapping() != null) {
healthMvcEndpoint
.addStatusMapping(this.healthMvcEndpointProperties.getMapping());
}
return healthMvcEndpoint;
}

// 注入關閉應用程式的MVC端點元件
@Bean
@ConditionalOnBean(ShutdownEndpoint.class) // 元件已例項化
@ConditionalOnEnabledEndpoint(value = "shutdown", enabledByDefault = false) // 端點已啟用
public ShutdownMvcEndpoint shutdownMvcEndpoint(ShutdownEndpoint delegate) {
return new ShutdownMvcEndpoint(delegate);
}

}

5.1、MVC端點登錄檔(MvcEndpoints)
/**
* 所有MVC端點元件的登錄檔,以及一組用於包裝尚未公開的MVC端點的現有端點例項的通用工廠。
*/
// 核心類 MVC端點登錄檔
public class MvcEndpoints implements ApplicationContextAware, InitializingBean {

/**
* 應用上下文
*/
private ApplicationContext applicationContext;

/**
* MVC端點集合
*/
private final Set<MvcEndpoint> endpoints = new HashSet<>();

/**
* MVC端點型別集合
*/
private Set<Class<?>> customTypes;


@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}

@Override
public void afterPropertiesSet() throws Exception {
// 現有的MVC端點列表
Collection<MvcEndpoint> existing = BeanFactoryUtils
.beansOfTypeIncludingAncestors(this.applicationContext, MvcEndpoint.class) // MVC端點
.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); // 端點MVC介面卡
// 端點路徑
String path = determinePath(endpoint,
this.applicationContext.getEnvironment());
if (path != null) {
adapter.setPath(path);
}
this.endpoints.add(adapter);
}
}
}

private Set<Class<?>> findEndpointClasses(Collection<MvcEndpoint> existing) {
Set<Class<?>> types = new HashSet<>();
for (MvcEndpoint endpoint : existing) {
Class<?> type = endpoint.getEndpointType(); // 端點型別
if (type != null) {
types.add(type);
}
}
return types;
}

// 核心方法 返回註冊的MVC端點集合
public Set<MvcEndpoint> getEndpoints() {
return this.endpoints;
}

/**
* 返回指定型別的MVC端點集合。
*/
@SuppressWarnings("unchecked")
public <E extends MvcEndpoint> Set<E> getEndpoints(Class<E> type) {
Set<E> result = new HashSet<>(this.endpoints.size());
for (MvcEndpoint candidate : this.endpoints) {
if (type.isInstance(candidate)) {
result.add((E) candidate);
}
}
return Collections.unmodifiableSet(result); // 不可修改的集合
}

// 通用的端點
private boolean isGenericEndpoint(Class<?> type) {
return !this.customTypes.contains(type)
&& !MvcEndpoint.class.isAssignableFrom(type);
}

private String determinePath(Endpoint<?> endpoint, Environment environment) {
// 配置屬性
ConfigurationProperties configurationProperties = AnnotationUtils
.findAnnotation(endpoint.getClass(), ConfigurationProperties.class);
if (configurationProperties != null) {
return environment.getProperty(configurationProperties.prefix() + ".path");
}
return null;
}

}

5.2、端點處理程式對映(EndpointHandlerMapping)
/**
* @RequestMapping的語義應該與普通的@Controller相同,
* 但是端點不應該被註釋為@Controller,否則它們將被正常的MVC機制對映。
* <p>
* <p>對映的目標之一是支援作為HTTP端點工作的端點,
* 但是當沒有HTTP伺服器(類路徑上沒有Spring MVC)時,仍然可以提供有用的服務介面。
* 注意:具有方法簽名的任何端點將在非Servlet環境下中斷。
*/
// 核心類 通過端點的邏輯標識將端點對映到URL的處理程式對映
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 accepts CORS
* requests based on the given {@code corsConfiguration}.
* @param endpoints the endpoints (MVC端點列表)
* @param corsConfiguration the CORS configuration for the endpoints
* @since 1.3.0
*/
public EndpointHandlerMapping(Collection<? extends MvcEndpoint> endpoints,
CorsConfiguration corsConfiguration) {
super(endpoints, corsConfiguration);
}

}

其抽象實現基類 AbstractEndpointHandlerMapping<E extends MvcEndpoint>
/**
* @RequestMapping的語義應該與普通的@Controller相同,
* 但是端點不應該被註釋為@Controller,否則它們將被正常的MVC機制對映。
* <p>對映的目標之一是支援作為HTTP端點工作的端點,
* 但是當沒有HTTP伺服器(類路徑上沒有Spring MVC)時,仍然可以提供有用的服務介面。
* 注意:具有方法簽名的任何端點將在非Servlet環境下中斷。
*
* @param <E> The endpoint type (端點型別)
*/
// 核心類 通過端點的邏輯標識將端點對映到URL的處理程式對映的抽象基類
public abstract class AbstractEndpointHandlerMapping<E extends MvcEndpoint>
extends RequestMappingHandlerMapping {

/**
* MVC端點集合
*/
private final Set<E> endpoints;

/**
* 安全處理程式攔截器
*/
private HandlerInterceptor securityInterceptor;

/**
* CORS配置
*/
private final CorsConfiguration corsConfiguration;

/**
* 端點的對映路徑字首
*/
private String prefix = "";

private boolean disabled = false;


/**
* Create a new {@link AbstractEndpointHandlerMapping} instance. All {@link Endpoint}s
* will be detected from the {@link ApplicationContext}. The endpoints will accepts
* CORS requests based on the given {@code corsConfiguration}.
* <p>將從應用上下文檢測到所有端點。
* @param endpoints the endpoints
* @param corsConfiguration the CORS configuration for the endpoints
* @since 1.3.0
*/
public AbstractEndpointHandlerMapping(Collection<? extends E> endpoints,
CorsConfiguration corsConfiguration) {
this.endpoints = new HashSet<>(endpoints);
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);
}

/**
* Post process the endpoint setting before they are used. Subclasses can add or
* modify the endpoints as necessary.
* <p>在使用之前,後處理端點設定。
* @param endpoints the endpoints to post process
*/
protected void postProcessEndpoints(Set<E> endpoints) {
}

@Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
if (!this.disabled) { // 端點處理程式被禁用
for (MvcEndpoint endpoint : this.endpoints) {
detectHandlerMethods(endpoint);
}
}
}

// 核心實現 註冊端點處理程式方法及其唯一對映
@Override
@Deprecated
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));
}
}

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); // MVC端點路徑
return (path == null ? null : getEndpointPatterns(path, mapping));
}

protected String getPath(MvcEndpoint endpoint) {
return endpoint.getPath();
}

// 核心實現 返回端點的路徑列表
private String[] getEndpointPatterns(String path, RequestMappingInfo mapping) {
// 路徑模式字首
String patternPrefix = StringUtils.hasText(this.prefix) ? this.prefix + path
: path;
// 預設的路徑集合
Set<String> defaultPatterns = mapping.getPatternsCondition().getPatterns();
if (defaultPatterns.isEmpty()) {
// 端點路徑
return new String[] { patternPrefix, patternPrefix + ".json" };
}
List<String> patterns = new ArrayList<>(defaultPatterns);
for (int i = 0; i < patterns.size(); i++) {
patterns.set(i, patternPrefix + patterns.get(i)); // 端點請求路徑
}
return patterns.toArray(new String[patterns.size()]);
}

// 新的端點路徑
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());
}

// 核心實現 獲取處理程式執行鏈
@Override
protected HandlerExecutionChain getHandlerExecutionChain(Object handler,
HttpServletRequest request) {
HandlerExecutionChain chain = super.getHandlerExecutionChain(handler, request);
if (this.securityInterceptor == null || CorsUtils.isCorsRequest(request)) {
return chain;
}
return addSecurityInterceptor(chain);
}

private HandlerExecutionChain addSecurityInterceptor(HandlerExecutionChain chain) {
// 處理程式攔截器
List<HandlerInterceptor> interceptors = new ArrayList<>();
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()]));
}

// 獲取端點的路徑
public String getPath(String endpoint) {
return this.prefix + endpoint;
}

// 返回MVC端點集合
public Set<E> getEndpoints() {
return Collections.unmodifiableSet(this.endpoints); // 不可修改的集合
}

}

5.3、元件存在條件(OnBeanCondition)
5.3.1、未注入元件條件(ConditionalOnMissingBean)
/**
* 僅當指定的元件型別或名稱尚未包含在{@link BeanFactory}中時才匹配的條件。
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnMissingBean {

/**
* The class type of bean that should be checked. The condition matches when each
* class specified is missing in the {@link ApplicationContext}.
* @return the class types of beans to check
*/
// 元件的型別
Class<?>[] value() default {};

String[] type() default {};

/**
* The class type of beans that should be ignored when identifying matching beans.
* @return the class types of beans to ignore
* @since 1.2.5
*/
Class<?>[] ignored() default {};

String[] ignoredType() default {};

// 裝飾元件的註解型別
Class<? extends Annotation>[] annotation() default {};

// 元件的名稱列表
String[] name() default {};

// 應用上下文層次結構的搜尋策略
SearchStrategy search() default SearchStrategy.ALL;

}

5.3.2、元件條件(ConditionalOnBean)
/**
* 僅當指定的元件型別或名稱已經包含在{@link BeanFactory}中時才匹配的條件
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnBean {

/**
* The class type of bean that should be checked. The condition matches when all of
* the classes specified are contained in the {@link ApplicationContext}.
* @return the class types of beans to check
*/
// 元件的型別
Class<?>[] value() default {};

String[] type() default {};

// 裝飾元件的註解型別
Class<? extends Annotation>[] annotation() default {};

// 元件的名稱列表
String[] name() default {};

// 應用上下文層次結構的搜尋策略
SearchStrategy search() default SearchStrategy.ALL;

}

5.3.3、啟用端點條件(ConditionalOnEnabledEndpoint)
/**
* 檢查端點是否啟用的條件。
* 如果endpoints.<name>.enabled屬性的值是true,則匹配。
*
* @since 1.2.4
*/
// 啟用端點上的條件
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@Documented
@Conditional(OnEnabledEndpointCondition.class)
public @interface ConditionalOnEnabledEndpoint {

// 端點的名稱
String value();

/**
* Returns whether or not the endpoint is enabled by default.
*/
boolean enabledByDefault() default true;

}

6、shutdown端點示例
6.1、關閉應用程式的端點(ShutdownEndpoint)
/**
* {@link Endpoint} to shutdown the {@link ApplicationContext}.
* <p>用於優雅地關閉應用上下文({@link ApplicationContext})的端點。
* 允許應用以優雅的方式關閉
*
* @author Dave Syer
* @author Christian Dupuis
* @author Andy Wilkinson
*/
@ConfigurationProperties(prefix = "endpoints.shutdown") // 屬性字首配置
public class ShutdownEndpoint extends AbstractEndpoint<Map<String, Object>>
implements ApplicationContextAware { // 應用上下文感知

/** 無關閉上下文資訊 */
private static final Map<String, Object> NO_CONTEXT_MESSAGE = Collections
.unmodifiableMap(Collections.<String, Object>singletonMap("message",
"No context to shutdown."));

/** 關閉資訊 */
private static final Map<String, Object> SHUTDOWN_MESSAGE = Collections
.unmodifiableMap(Collections.<String, Object>singletonMap("message",
"Shutting down, bye..."));

/**
* 可配置的應用上下文
*/
private ConfigurableApplicationContext context;


/**
* Create a new {@link ShutdownEndpoint} instance.
*/
public ShutdownEndpoint() {
super("shutdown", true, false);
}


// 核心實現 新啟執行緒來關閉應用上下文,並釋放所有資源和鎖
@Override
public Map<String, Object> invoke() {
if (this.context == null) {
return NO_CONTEXT_MESSAGE;
}
try {
return SHUTDOWN_MESSAGE;
}
finally {
Thread thread = new Thread(() -> {
try {
Thread.sleep(500L); // 使當前正在執行的執行緒休眠(500ms)
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt(); // 如果出現中斷異常,則中斷當前執行緒
}
ShutdownEndpoint.this.context.close(); // 關閉應用上下文,並釋放所有資源和鎖
});
thread.setContextClassLoader(getClass().getClassLoader()); // 本類的類載入器
thread.start();
}
}

// 核心實現 設定可配置的應用上下文
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
if (context instanceof ConfigurableApplicationContext) {
this.context = (ConfigurableApplicationContext) context;
}
}

}

6.2、關閉應用程式的MVC端點(ShutdownMvcEndpoint)
/**
* 暴露關閉應用上下文端點({@link ShutdownEndpoint})為MVC端點({@link MvcEndpoint})的介面卡。
*/
// 關閉應用程式的MVC端點
@ConfigurationProperties(prefix = "endpoints.shutdown") // 屬性字首配置
public class ShutdownMvcEndpoint extends EndpointMvcAdapter {

public ShutdownMvcEndpoint(ShutdownEndpoint delegate) {
super(delegate); // 委託代理
}

// 核心實現 以HTTP POST方式呼叫
@PostMapping(produces = { ActuatorMediaTypes.APPLICATION_ACTUATOR_V1_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
@ResponseBody
@Override
public Object invoke() {
if (!getDelegate().isEnabled()) { // 端點被禁用
return getDisabledResponse();
}
return super.invoke(); // 向上呼叫,鏈式模式
}

}

至此,HTTP端點實現原理就全部分析完成。