dubboProperties = filterDubboProperties(environment);
// <2.2> 添加到 Dubbo Properties 中
ConfigUtils.getProperties().putAll(dubboProperties);
if (logger.isInfoEnabled()) {
logger.info("Dubbo Config was overridden by externalized configuration {}", dubboProperties);
}
} else {
if (logger.isInfoEnabled()) {
logger.info("Disable override Dubbo Config caused by property {} = {}", OVERRIDE_CONFIG_PROPERTY_NAME, override);
}
}
}
}
```
- `<1>` 处,获得 `"dubbo.config.override"` 属性对应的值。默认情况下为 `true` 。代码如下:
```
// DubboUtils.java
/**
* The property name of override Dubbo config
*
* The default value is {@link #DEFAULT_OVERRIDE_CONFIG_PROPERTY_VALUE}
*/
public static final String OVERRIDE_CONFIG_PROPERTY_NAME = DUBBO_CONFIG_PREFIX + PROPERTY_NAME_SEPARATOR + "override";
/**
* The default property value of override Dubbo config
*/
public static final boolean DEFAULT_OVERRIDE_CONFIG_PROPERTY_VALUE = true;
```
- `<2>` 处,如果要重写,则覆盖添加到 Dubbo Properties 中。
- `<2.1>` 处,调用 `DubboUtils#filterDubboProperties(ConfigurableEnvironment environment)` 方法,从 `environment` 中,提取 `"dubbo."` 开头的配置。代码如下:
```
// DubboUtils.java
/**
* The separator of property name
*/
public static final String PROPERTY_NAME_SEPARATOR = ".";
/**
* The prefix of property name of Dubbo
*/
public static final String DUBBO_PREFIX = "dubbo";
/**
* Filters Dubbo Properties from {@link ConfigurableEnvironment}
*
* @param environment {@link ConfigurableEnvironment}
* @return Read-only SortedMap
*/
public static SortedMap filterDubboProperties(ConfigurableEnvironment environment) {
SortedMap dubboProperties = new TreeMap<>();
// 获得所有的配置
Map properties = EnvironmentUtils.extractProperties(environment);
// 遍历配置,如果以 "dubbo." 开头,则添加到 dubboProperties 中
for (Map.Entry entry : properties.entrySet()) {
String propertyName = entry.getKey();
if (propertyName.startsWith(DUBBO_PREFIX + PROPERTY_NAME_SEPARATOR)
&& entry.getValue() != null) {
dubboProperties.put(propertyName, entry.getValue().toString());
}
}
// 返回 dubboProperties
return Collections.unmodifiableSortedMap(dubboProperties);
}
```
- 其中,`EnvironmentUtils#extractProperties(ConfigurableEnvironment environment)` 方法,获得所有的配置。考虑到篇幅就不赘述,艿艿已经添加注释,点击 [链接](https://github.com/YunaiV/incubator-dubbo-spring-boot-project/blob/master/dubbo-spring-boot-autoconfigure/src/main/java/com/alibaba/boot/dubbo/util/EnvironmentUtils.java) 查看。
- `<2.2>` 处,调用 Dubbo 的 `ConfigUtils#getProperties()` 方法,获得 Dubbo Properties 。然后再将 `dubboProperties` 变量,添加到 Dubbo Properties 中。
### 4.6 AwaitingNonWebApplicationListener
`com.alibaba.boot.dubbo.context.event.AwaitingNonWebApplicationListener` ,实现 SmartApplicationListener 接口,实现在非 Web 的环境下,提供 JVM 不退出关闭的功能,即 JVM 一直运行着。
> 胖友可以试试,启动一个非 Web 环境的 Spring Boot 应用,然后会发现,JVM 会在启动完 Spring Boot 应用后,自动关闭。
#### 4.6.1 supportsEventType
实现 `#supportsEventType(Class extends ApplicationEvent> eventType)` 方法,判断支持的事件类型是 ApplicationReadyEvent 和 ContextClosedEvent 。代码如下:
```
// AwaitingNonWebApplicationListener.java
private static final Class extends ApplicationEvent>[] SUPPORTED_APPLICATION_EVENTS =
of(ApplicationReadyEvent.class, ContextClosedEvent.class);
@Override
public boolean supportsEventType(Class extends ApplicationEvent> eventType) {
return ObjectUtils.containsElement(SUPPORTED_APPLICATION_EVENTS, eventType);
}
private static T[] of(T... values) {
return values;
}
```
- 为什么呢,我们接着往下看。
#### 4.6.2 supportsSourceType
实现 `#supportsSourceType(Class> sourceType)` 方法,判断支持的事件来源。代码如下:
```
// AwaitingNonWebApplicationListener.java
@Override
public boolean supportsSourceType(Class> sourceType) {
return true;
}
```
- 全部返回 `true` ,意味支持所有的事件来源。
#### 4.6.3 onApplicationEvent
```
// AwaitingNonWebApplicationListener.java
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationReadyEvent) {
onApplicationReadyEvent((ApplicationReadyEvent) event); // <1>
} else if (event instanceof ContextClosedEvent) {
onContextClosedEvent((ContextClosedEvent) event); // <2>
}
}
```
- `<1>` 处,当是 ApplicationReadyEvent 事件时,调用 `#onApplicationReadyEvent(ApplicationReadyEvent event)` 方法,处理 ApplicationReadyEvent 事件。详细解析,见 [「4.6.3.1 onApplicationReadyEvent」](http://svip.iocoder.cn/Dubbo/configuration-Externalized/#) 。
- `<2>` 处,当是 ContextClosedEvent 事件时,调用 `#onApplicationReadyEvent(ContextClosedEvent event)` 方法,处理 ApplicationReadyEvent 事件。详细解析,见 [「4.6.3.2 onContextClosedEvent」](http://svip.iocoder.cn/Dubbo/configuration-Externalized/#) 。
##### 4.6.3.1 onApplicationReadyEvent
`#onApplicationReadyEvent(ApplicationReadyEvent event)` 方法,处理 ApplicationReadyEvent 事件。代码如下:
```
// AwaitingNonWebApplicationListener.java
protected void onApplicationReadyEvent(ApplicationReadyEvent event) {
// <1> 如果是 Web 环境,则直接返回
final SpringApplication springApplication = event.getSpringApplication();
if (!WebApplicationType.NONE.equals(springApplication.getWebApplicationType())) {
return;
}
// <2> 启动一个用户线程,从而实现等待
await();
}
```
- `<1>` 处,如果是 Web 环境,则直接返回。因为,已经提供了 JVM 不退出关闭的功能。
- `<2>` 处,调用 `#await()` 方法,启动一个用户线程,从而实现等待。代码如下:
```
// AwaitingNonWebApplicationListener.java
/**
* 是否已经等待完成
*/
private static final AtomicBoolean awaited = new AtomicBoolean(false);
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
protected void await() {
// has been waited, return immediately
// 如果已经处于阻塞等待,直接返回
if (awaited.get()) {
return;
}
// 创建任务,实现阻塞
executorService.execute(() -> executeMutually(() -> {
while (!awaited.get()) {
if (logger.isInfoEnabled()) {
logger.info(" [Dubbo] Current Spring Boot Application is await...");
}
try {
condition.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}));
}
private void executeMutually(Runnable runnable) {
try {
lock.lock();
// 执行 Runnable
runnable.run();
} finally {
lock.unlock();
}
}
```
- 基于 Lock + Condition 实现等待通知。
- `#executeMutually(Runnable runnable)` 方法,被 `executorService` 创建任务所调用。而该任务因为调用 `Condition#await()` 方法,阻塞等待。那么此时,JVM 至少有一个该用户线程未运行结束,那么此时 [JVM 关闭的条件](https://blog.csdn.net/hongxingxiaonan/article/details/50528041) 不被满足,所以就不会退出。
##### 4.6.3.2 onApplicationReadyEvent
`#onApplicationReadyEvent(ContextClosedEvent event)` 方法,处理 ContextClosedEvent 事件。代码如下:
```
// AwaitingNonWebApplicationListener.java
protected void onContextClosedEvent(ContextClosedEvent event) {
// <1> 释放
release();
// <2> 关闭线程池
shutdown();
}
```
- `<1>` 处,调用 `#release()` 方法,进行释放。代码如下:
```
// AwaitingNonWebApplicationListener.java
protected void release() {
executeMutually(() -> {
// CAS 设置 awaited 为 true
while (awaited.compareAndSet(false, true)) {
if (logger.isInfoEnabled()) {
logger.info(" [Dubbo] Current Spring Boot Application is about to shutdown...");
}
// 通知 Condition
condition.signalAll();
}
});
}
```
- 通过调用 `Condition#signalAll()` 方法,通知 Condition 。从而在 [「4.6.3.1 onApplicationReadyEvent」](http://svip.iocoder.cn/Dubbo/configuration-Externalized/#) 中,启动的线程的阻塞,进行停止。
- `<2>` 处,调用 `#shutdown()` 方法,关闭线程池。代码如下:
```
// AwaitingNonWebApplicationListener.java
private void shutdown() {
if (!executorService.isShutdown()) {
// Shutdown executorService
executorService.shutdown();
}
}
```
## 5. `dubbo-spring-boot-actuator` 源码
`dubbo-spring-boot-autoconfigure` 模块,所有类如下图:[![`dubbo-spring-boot-autoconfigure` 模块](http://static2.iocoder.cn/images/Dubbo/2019_02_11/09.jpg)](http://static2.iocoder.cn/images/Dubbo/2019_02_11/09.jpg)`dubbo-spring-boot-autoconfigure` 模块
### 5.1 使用指南
使用时,需要导入 `dubbo-spring-boot-actuator` 依赖。即如下:
```
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-actuator
com.alibaba.boot
dubbo-spring-boot-actuator
```
观看本小节,胖友需要对 Spring Boot Actuator 有相关的了解。如果不知道,可以看看 [《一起来学 SpringBoot 2.x | 第十四篇:强大的 Actuator 服务监控与管理》](http://www.iocoder.cn/Spring-Boot/battcn/v2-actuator-introduce/) 文章。
另外,[《Dubbo 官方文档 —— Dubbo Spring Boot Production-Ready》](https://github.com/apache/incubator-dubbo-spring-boot-project/tree/master/dubbo-spring-boot-actuator) 文章,也是需要先瞅瞅的。
如果胖友使用的是 Spring Boot 2,有一个坑要注意,因为 [《Spring Boot 2.0 的 Actuator 只暴露 health 和 info》](https://blog.csdn.net/qq_20367813/article/details/79154981) ,所以需要手动在配置文件中,添加要开启的 Dubbo Endpoint 。例如:
```
# application.properties
management.endpoints.web.exposure.include=health,info,dubbo,dubboconfigs
```
- 此时,我们多开启了 `dubbo` 和 `dubboconfigs` 这两个 EndPoint 。T T 坑了自己好久~
> 如下开始,是 `autoconfigure` 包下。
### 5.2 DubboEndpointsAutoConfiguration
`com.alibaba.boot.dubbo.actuate.autoconfigure.DubboEndpointsAutoConfiguration` ,Dubbo Endpoint 自动配置类。代码如下:
```
// DubboEndpointsAutoConfiguration.java
@Configuration
@PropertySource(
name = "Dubbo Endpoints Default Properties",
value = "classpath:/META-INF/dubbo-endpoins-default.properties") // 导入该配置文件
public class DubboEndpointsAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
public DubboEndpoint dubboEndpoint() {
return new DubboEndpoint();
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
public DubboConfigsMetadataEndpoint dubboConfigsMetadataEndpoint() {
return new DubboConfigsMetadataEndpoint();
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
public DubboPropertiesEndpoint dubboPropertiesEndpoint() {
return new DubboPropertiesEndpoint();
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
public DubboReferencesMetadataEndpoint dubboReferencesMetadataEndpoint() {
return new DubboReferencesMetadataEndpoint();
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
public DubboServicesMetadataEndpoint dubboServicesMetadataEndpoint() {
return new DubboServicesMetadataEndpoint();
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
public DubboShutdownEndpoint dubboShutdownEndpoint() {
return new DubboShutdownEndpoint();
}
}
```
- 每个方法,创建一个 Dubbo Endpint Bean 。一共有 6 个。
- `@PropertySource`注解,导入`"classpath:/META-INF/dubbo-endpoins-default.properties"`配置文件。代码如下:
```
# dubbo-endpoins-default.properties
# Dubbo Endpoints Default Properties is loaded by @PropertySource with low order,
# those values of properties can be override by higher PropertySource
# @see DubboEndpointsAutoConfiguration
# Set enabled for Dubbo Endpoints 设置 Dubbo Endpoints 是否开启
management.endpoint.dubbo.enabled = true
management.endpoint.dubboshutdown.enabled = false
management.endpoint.dubboconfigs.enabled = true
management.endpoint.dubboservices.enabled = false
management.endpoint.dubboreferences.enabled = false
management.endpoint.dubboproperties.enabled = true
# "management.endpoints.web.base-path" should not be configured in this file
# Re-defines path-mapping of Dubbo Web Endpoints 重命名 Dubbo Web Endpoints 路径
management.endpoints.web.path-mapping.dubboshutdown = dubbo/shutdown
management.endpoints.web.path-mapping.dubboconfigs = dubbo/configs
management.endpoints.web.path-mapping.dubboservices = dubbo/services
management.endpoints.web.path-mapping.dubboreferences = dubbo/references
management.endpoints.web.path-mapping.dubboproperties = dubbo/properties
```
### 5.3 DubboHealthIndicatorAutoConfiguration
`com.alibaba.boot.dubbo.actuate.autoconfigure.DubboHealthIndicatorAutoConfiguration` ,Dubbo Health Indicator 自动配置类。代码如下:
```
// DubboHealthIndicatorAutoConfiguration.java
@Configuration
@ConditionalOnClass({HealthIndicator.class}) // 存在 HealthIndicator 类的情况
@AutoConfigureBefore({EndpointAutoConfiguration.class}) // 在 EndpointAutoConfiguration 自动配置类之前初始化
@AutoConfigureAfter(DubboAutoConfiguration.class) // 在 DubboAutoConfiguration 自动配置类之后初始化
@ConditionalOnEnabledHealthIndicator("dubbo")
@EnableConfigurationProperties(DubboHealthIndicatorProperties.class) // 自动配置 DubboHealthIndicatorProperties 类
public class DubboHealthIndicatorAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public DubboHealthIndicator dubboHealthIndicator() {
return new DubboHealthIndicator();
}
}
```
- 每个注解,看后面的代码注释。
- 唯一的方法,创建 DubboHealthIndicator Bean 对象。详细解析,见 [「5.5 DubboHealthIndicator」](http://svip.iocoder.cn/Dubbo/configuration-Externalized/#) 。
### 5.4 DubboHealthIndicatorProperties
`com.alibaba.boot.dubbo.actuate.health.DubboHealthIndicatorProperties` ,Dubbo Health Indicator Properties 类。代码代码如下:
```
// DubboHealthIndicatorProperties.java
@ConfigurationProperties(prefix = PREFIX, ignoreUnknownFields = false) // "management.health.dubbo" 开头的配置
public class DubboHealthIndicatorProperties {
/**
* The prefix of {@link DubboHealthIndicatorProperties}
*/
public static final String PREFIX = "management.health.dubbo";
private Status status = new Status();
// ... 省略 setting/getting 方法
/**
* The nested class for {@link StatusChecker}'s names
*
* registry=com.alibaba.dubbo.registry.status.RegistryStatusChecker
* spring=com.alibaba.dubbo.config.spring.status.SpringStatusChecker
* datasource=com.alibaba.dubbo.config.spring.status.DataSourceStatusChecker
* memory=com.alibaba.dubbo.common.status.support.MemoryStatusChecker
* load=com.alibaba.dubbo.common.status.support.LoadStatusChecker
* server=com.alibaba.dubbo.rpc.protocol.dubbo.status.ServerStatusChecker
* threadpool=com.alibaba.dubbo.rpc.protocol.dubbo.status.ThreadPoolStatusChecker
*
*
* @see StatusChecker
*/
public static class Status {
/**
* The defaults names of {@link StatusChecker}
*
* The defaults : "memory", "load"
*/
private Set defaults = new LinkedHashSet<>(Arrays.asList("memory", "load"));
/**
* The extra names of {@link StatusChecker}
*
* 配置的 "management.health.dubbo.extras" 集合
*
* 每个元素,是 StatusChecker 的实现类
*/
private Set extras = new LinkedHashSet<>();
// ... 省略 setting/getting 方法
}
}
```
- 读取以 `"management.health.dubbo"` 开头的配置。
> 如下开始,是 `health` 包下。
### 5.5 DubboHealthIndicator
`com.alibaba.boot.dubbo.actuate.health.DubboHealthIndicator` ,继承 AbstractHealthIndicator 抽象类,Dubbo Health Indicator 实现类。代码如下:
#### 5.4.1 doHealthCheck
实现 `#doHealthCheck(Health.Builder builder)` 方法,执行健康检查。代码如下:
> 在请求 `/actuator/health` 接口时,也会调用该方法。
```
// DubboHealthIndicator.java
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
// <1> 获得 StatusChecker 对应的 Dubbo ExtensionLoader 对象
ExtensionLoader extensionLoader = getExtensionLoader(StatusChecker.class);
// <2> 解析 StatusChecker 的名字的 Map
Map statusCheckerNamesMap = resolveStatusCheckerNamesMap();
// <3> 声明 hasError、hasUnknown 变量
boolean hasError = false; // 是否有错误的返回
boolean hasUnknown = false; // 是否有未知的返回
// Up first
// <4> 先 builder 标记状态是 UP
builder.up();
// <5> 遍历 statusCheckerNamesMap 元素
for (Map.Entry entry : statusCheckerNamesMap.entrySet()) {
// <6.1> 获得 StatusChecker 的名字
String statusCheckerName = entry.getKey();
// <6.2> 获得 source
String source = entry.getValue();
// <6.3> 获得 StatusChecker 对象
StatusChecker checker = extensionLoader.getExtension(statusCheckerName);
// <6.4> 执行校验
com.alibaba.dubbo.common.status.Status status = checker.check();
// <7.1> 获得校验结果
com.alibaba.dubbo.common.status.Status.Level level = status.getLevel();
// <7.2> 如果是 ERROR 检验结果,则标记 hasError 为 true ,并标记 builder 状态为 down
if (!hasError // 不存在 hasError 的时候
&& level.equals(com.alibaba.dubbo.common.status.Status.Level.ERROR)) {
hasError = true;
builder.down();
}
// <7.3> 如果是 UNKNOWN 检验结果,则标记 hasUnknown 为 true ,并标记 builder 状态为 unknown
if (!hasError && !hasUnknown // 不存在 hasError 且不存在 hasUnknown
&& level.equals(com.alibaba.dubbo.common.status.Status.Level.UNKNOWN)) {
hasUnknown = true;
builder.unknown();
}
// <8.1> 创建 detail Map
Map detail = new LinkedHashMap<>();
// <8.2> 设置 detail 属性值
detail.put("source", source);
detail.put("status", status); // 校验结果
// <8.3> 添加到 builder 中
builder.withDetail(statusCheckerName, detail);
}
}
```
- 大体比较简单,胖友顺着注释来瞅瞅即可。
- `<2>` 处,调用 `#resolveStatusCheckerNamesMap()` 方法,解析 StatusChecker 的名字的 Map 。因为这个对后续逻辑非常关键,所以胖友先跳到 [「5.4.2 resolveStatusCheckerNamesMap」](http://svip.iocoder.cn/Dubbo/configuration-Externalized/#) 中。看完之后,在回到此处。
- 最终返回 `builder` 的结果,如下图:[![`builder` 结果](http://static2.iocoder.cn/images/Dubbo/2019_02_11/11.jpg)](http://static2.iocoder.cn/images/Dubbo/2019_02_11/11.jpg)`builder` 结果
#### 5.4.2 resolveStatusCheckerNamesMap
`#resolveStatusCheckerNamesMap()` 方法,解析 StatusChecker 的名字的 Map。代码如下:
```
// DubboHealthIndicator.java
/**
* Resolves the map of {@link StatusChecker}'s name and its' source.
*
* 解析 StatusChecker 的名字的 Map
*
* KEY:StatusChecker 的名字
* VALUE:配置的来源
*
* @return non-null {@link Map}
*/
protected Map resolveStatusCheckerNamesMap() {
// 创建 Map
Map statusCheckerNamesMap = new LinkedHashMap<>();
// <1> 从 DubboHealthIndicatorProperties 中获取
statusCheckerNamesMap.putAll(resolveStatusCheckerNamesMapFromDubboHealthIndicatorProperties());
// <2> 从 ProtocolConfig 中获取
statusCheckerNamesMap.putAll(resolveStatusCheckerNamesMapFromProtocolConfigs());
// <3> 从 ProviderConfig 中获取
statusCheckerNamesMap.putAll(resolveStatusCheckerNamesMapFromProviderConfig());
return statusCheckerNamesMap;
}
```
- `<1>`处,调用`#resolveStatusCheckerNamesMapFromDubboHealthIndicatorProperties()`方法,从 DubboHealthIndicatorProperties 中获取。代码如下:
```
// DubboHealthIndicator.java
@Autowired
private DubboHealthIndicatorProperties dubboHealthIndicatorProperties;
private Map resolveStatusCheckerNamesMapFromDubboHealthIndicatorProperties() {
// 获得 DubboHealthIndicatorProperties.Status
DubboHealthIndicatorProperties.Status status = dubboHealthIndicatorProperties.getStatus();
// 创建 Map
Map statusCheckerNamesMap = new LinkedHashMap<>();
// 1. 读取 defaults 属性
for (String statusName : status.getDefaults()) {
statusCheckerNamesMap.put(statusName, PREFIX + ".status.defaults");
}
// 2. 读取 extras 属性
for (String statusName : status.getExtras()) {
statusCheckerNamesMap.put(statusName, PREFIX + ".status.extras");
}
return statusCheckerNamesMap;
}
```
- `<2>`处,调用`#resolveStatusCheckerNamesMapFromProtocolConfigs()`方法,从 ProtocolConfig 中获取。代码如下:
```
// DubboHealthIndicator.java
@Autowired(required = false)
private Map protocolConfigs = Collections.emptyMap();
private Map resolveStatusCheckerNamesMapFromProtocolConfigs() {
// 创建 Map
Map statusCheckerNamesMap = new LinkedHashMap<>();
// 遍历 protocolConfigs
for (Map.Entry entry : protocolConfigs.entrySet()) {
// 获得 Bean 的名字
String beanName = entry.getKey();
// 获得 ProtocolConfig 对象
ProtocolConfig protocolConfig = entry.getValue();
// 获得 ProtocolConfig 的 StatusChecker 的名字的集合
Set statusCheckerNames = getStatusCheckerNames(protocolConfig);
// 遍历 statusCheckerNames 数组
for (String statusCheckerName : statusCheckerNames) {
// 构建 source 属性
String source = buildSource(beanName, protocolConfig);
// 添加到 statusCheckerNamesMap 中
statusCheckerNamesMap.put(statusCheckerName, source);
}
}
return statusCheckerNamesMap;
}
private Set getStatusCheckerNames(ProtocolConfig protocolConfig) {
String status = protocolConfig.getStatus();
return StringUtils.commaDelimitedListToSet(status);
}
private Set getStatusCheckerNames(ProviderConfig providerConfig) {
String status = providerConfig.getStatus();
return StringUtils.commaDelimitedListToSet(status);
}
private String buildSource(String beanName, Object bean) {
return beanName + "@" + bean.getClass().getSimpleName() + ".getStatus()";
}
```
- `<3>`处,调用`#resolveStatusCheckerNamesMapFromProviderConfig()`方法,从 ProviderConfig 中获取。代码如下:
```
// DubboHealthIndicator.java
@Autowired(required = false)
private Map providerConfigs = Collections.emptyMap();
private Map resolveStatusCheckerNamesMapFromProviderConfig() {
// 创建 Map
Map statusCheckerNamesMap = new LinkedHashMap<>();
// 遍历 providerConfigs
for (Map.Entry entry : providerConfigs.entrySet()) {
// 获得 Bean 的名字
String beanName = entry.getKey();
// 获得 ProviderConfig 对象
ProviderConfig providerConfig = entry.getValue();
// 获得 ProtocolConfig 的 StatusChecker 的名字的集合
Set statusCheckerNames = getStatusCheckerNames(providerConfig);
// 遍历 statusCheckerNames 数组
for (String statusCheckerName : statusCheckerNames) {
// 构建 source 属性
String source = buildSource(beanName, providerConfig);
// 添加到 statusCheckerNamesMap 中
statusCheckerNamesMap.put(statusCheckerName, source);
}
}
return statusCheckerNamesMap;
}
```
- 最终的结果,示例如下图:[![`statusCheckerNamesMap` 属性](http://static2.iocoder.cn/images/Dubbo/2019_02_11/10.jpg)](http://static2.iocoder.cn/images/Dubbo/2019_02_11/10.jpg)`statusCheckerNamesMap` 属性
> 如下开始,是 `endpoint` 包下。
### 5.6 AbstractDubboEndpoint
`com.alibaba.boot.dubbo.actuate.endpoint.AbstractDubboEndpoint` ,实现 ApplicationContextAware、EnvironmentAware 接口,Dubbo Endpoint 抽象类,提供给子类工具方法。
#### 5.6.1 基本属性
```
// AbstractDubboEndpoint.java
protected ApplicationContext applicationContext;
protected ConfigurableEnvironment environment;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void setEnvironment(Environment environment) {
if (environment instanceof ConfigurableEnvironment) {
this.environment = (ConfigurableEnvironment) environment;
}
}
```
#### 5.6.2 resolveBeanMetadata
`#resolveBeanMetadata(Object bean)` 方法,获得 Bean 的元数据。代码如下:
```
// AbstractDubboEndpoint.java
protected Map resolveBeanMetadata(final Object bean) {
// 创建 Map
final Map beanMetadata = new LinkedHashMap<>();
try {
// 获得 BeanInfo 对象
BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
// 获得 PropertyDescriptor 数组
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
// 遍历 PropertyDescriptor 数组
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
// 获得 Method 对象
Method readMethod = propertyDescriptor.getReadMethod();
// 读取属性,添加到 beanMetadata 中
if (readMethod != null && isSimpleType(propertyDescriptor.getPropertyType())) {
String name = Introspector.decapitalize(propertyDescriptor.getName());
Object value = readMethod.invoke(bean);
beanMetadata.put(name, value);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return beanMetadata;
}
private static boolean isSimpleType(Class> type) {
return isPrimitiveOrWrapper(type) // 基本类型 or 包装类型
|| type == String.class
|| type == BigDecimal.class
|| type == BigInteger.class
|| type == Date.class
|| type == URL.class
|| type == Class.class
;
}
```
- 目的是读取 Bean 的基本属性。示例如下图:[![`beanMetadata` 结果](http://static2.iocoder.cn/images/Dubbo/2019_02_11/12.jpg)](http://static2.iocoder.cn/images/Dubbo/2019_02_11/12.jpg)`beanMetadata` 结果
#### 5.6.3 getServiceBeansMap
`#getServiceBeansMap()` 方法,获得所有 ServiceBean 。代码如下:
```
// AbstractDubboEndpoint.java
protected Map getServiceBeansMap() {
return BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ServiceBean.class);
}
```
#### 5.6.4 getProtocolConfigsBeanMap
`#getProtocolConfigsBeanMap()` 方法,获得所有 ProtocolConfig 。代码如下:
```
// AbstractDubboEndpoint.java
protected Map getProtocolConfigsBeanMap() {
return BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class);
}
```
#### 5.6.5 getReferenceAnnotationBeanPostProcessor
`#getReferenceAnnotationBeanPostProcessor()` 方法,获得 ReferenceAnnotationBeanPostProcessor Bean 对象。代码如下:
```
// AbstractDubboEndpoint.java
protected ReferenceAnnotationBeanPostProcessor getReferenceAnnotationBeanPostProcessor() {
return applicationContext.getBean(ReferenceAnnotationBeanPostProcessor.BEAN_NAME, ReferenceAnnotationBeanPostProcessor.class);
}
```
### 5.7 DubboEndpoint
`com.alibaba.boot.dubbo.actuate.endpoint.DubboEndpoint` ,Dubbo Endpoint ,获得 Dubbo Meta Data(元数据)。代码如下:
```
// DubboEndpoint.java
/**
* Actuator {@link Endpoint} to expose Dubbo Meta Data
*/
@Endpoint(id = "dubbo")
public class DubboEndpoint {
@ReadOperation
public Map invoke() {
// 创建 Map
Map metaData = new LinkedHashMap<>();
// timestamp
metaData.put("timestamp", System.currentTimeMillis());
// versions
Map versions = new LinkedHashMap<>();
versions.put("dubbo-spring-boot", Version.getVersion(DubboUtils.class, "1.0.0"));
versions.put("dubbo", Version.getVersion());
// urls
Map urls = new LinkedHashMap<>();
urls.put("dubbo", DUBBO_GITHUB_URL);
urls.put("mailing-list", DUBBO_MAILING_LIST);
urls.put("github", DUBBO_SPRING_BOOT_GITHUB_URL);
urls.put("issues", DUBBO_SPRING_BOOT_ISSUES_URL);
urls.put("git", DUBBO_SPRING_BOOT_GIT_URL);
metaData.put("versions", versions);
metaData.put("urls", urls);
return metaData;
}
}
```
### 5.8 DubboConfigsMetadataEndpoint
`com.alibaba.boot.dubbo.actuate.endpoint.DubboConfigsMetadataEndpoint` ,继承 AbstractDubboEndpoint 抽象类,获得 所有的 Dubbo 配置类的元数据。代码如下:
```
// DubboConfigsMetadataEndpoint.java
/**
* Dubbo Configs Metadata {@link Endpoint}
*/
@Endpoint(id = "dubboconfigs")
public class DubboConfigsMetadataEndpoint extends AbstractDubboEndpoint {
@ReadOperation
public Map>> configs() {
// 创建 Map
// KEY:获得类的简称。例如:ApplicationConfig、ConsumerConfig
// KEY2:Bean 的名称
// VALUE:Bean 的元数据
Map>> configsMap = new LinkedHashMap<>();
// 遍历每个配置类,添加其的 Bean 们,到 configsMap 中
addDubboConfigBeans(ApplicationConfig.class, configsMap);
addDubboConfigBeans(ConsumerConfig.class, configsMap);
addDubboConfigBeans(MethodConfig.class, configsMap);
addDubboConfigBeans(ModuleConfig.class, configsMap);
addDubboConfigBeans(MonitorConfig.class, configsMap);
addDubboConfigBeans(ProtocolConfig.class, configsMap);
addDubboConfigBeans(ProviderConfig.class, configsMap);
addDubboConfigBeans(ReferenceConfig.class, configsMap);
addDubboConfigBeans(RegistryConfig.class, configsMap);
addDubboConfigBeans(ServiceConfig.class, configsMap);
return configsMap;
}
private void addDubboConfigBeans(Class extends AbstractConfig> dubboConfigClass, Map>> configsMap) {
// 获得指定类 dubboConfigClass 的 Map
Map dubboConfigBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, dubboConfigClass);
// 获得类的简称。例如:ApplicationConfig、ConsumerConfig
String name = dubboConfigClass.getSimpleName();
// 创建 Map
Map> beansMetadata = new TreeMap<>();
// 遍历 dubboConfigBeans 数组
for (Map.Entry entry : dubboConfigBeans.entrySet()) {
// 获得 Bean 的名字
String beanName = entry.getKey();
// 获得 Bean 的元数据
AbstractConfig configBean = entry.getValue();
Map configBeanMeta = super.resolveBeanMetadata(configBean);
// 添加到 beansMetadata 中
beansMetadata.put(beanName, configBeanMeta);
}
// 添加到 configsMap 中
configsMap.put(name, beansMetadata);
}
}
```
### 5.7 DubboPropertiesEndpoint
`com.alibaba.boot.dubbo.actuate.endpoint.DubboPropertiesEndpoint` ,继承 AbstractDubboEndpoint 抽象类,获得 Dubbo Properties 。代码如下:
```
// DubboPropertiesEndpoint.java
/**
* Dubbo Properties {@link Endpoint}
*/
@Endpoint(id = "dubboproperties")
public class DubboPropertiesEndpoint extends AbstractDubboEndpoint {
@ReadOperation
public SortedMap properties() {
return DubboUtils.filterDubboProperties(environment);
}
}
```
### 5.8 DubboReferencesMetadataEndpoint
`com.alibaba.boot.dubbo.actuate.endpoint.DubboReferencesMetadataEndpoint` ,继承 AbstractDubboEndpoint 抽象类,获得所有的 Dubbo `@Reference` Bean 的元数据。代码如下:
```
// DubboReferencesMetadataEndpoint.java
/**
* Dubbo {@link Reference} Metadata {@link Endpoint}
*/
@Endpoint(id = "dubboreferences")
public class DubboReferencesMetadataEndpoint extends AbstractDubboEndpoint {
@ReadOperation
public Map> references() {
// 创建 Map
// KEY:Bean 的名字
// VALUE:Bean 的元数据
Map> referencesMetadata = new LinkedHashMap<>();
// 获得 ReferenceAnnotationBeanPostProcessor Bean 对象
ReferenceAnnotationBeanPostProcessor beanPostProcessor = super.getReferenceAnnotationBeanPostProcessor();
// injected Field ReferenceBean Cache
referencesMetadata.putAll(buildReferencesMetadata(beanPostProcessor.getInjectedFieldReferenceBeanMap()));
// injected Method ReferenceBean Cache
referencesMetadata.putAll(buildReferencesMetadata(beanPostProcessor.getInjectedMethodReferenceBeanMap()));
return referencesMetadata;
}
private Map> buildReferencesMetadata(Map> injectedElementReferenceBeanMap) {
// 创建 Map
// KEY:Bean 的名字
// VALUE:Bean 的元数据
Map> referencesMetadata = new LinkedHashMap<>();
// 遍历 injectedElementReferenceBeanMap 元素
for (Map.Entry> entry : injectedElementReferenceBeanMap.entrySet()) {
InjectionMetadata.InjectedElement injectedElement = entry.getKey();
// 获得 ReferenceBean 对象
ReferenceBean> referenceBean = entry.getValue();
// 获得 Bean 元数据
Map beanMetadata = super.resolveBeanMetadata(referenceBean);
// 获得 invoker 属性
beanMetadata.put("invoker", super.resolveBeanMetadata(referenceBean.get()));
// 添加到 referencesMetadata 中
referencesMetadata.put(String.valueOf(injectedElement.getMember()), beanMetadata);
}
return referencesMetadata;
}
}
```
### 5.9 DubboServicesMetadataEndpoint
`com.alibaba.boot.dubbo.actuate.endpoint.DubboServicesMetadataEndpoint` ,继承 AbstractDubboEndpoint 抽象类,获得所有的 Dubbo `@Service` Bean 的元数据。代码如下:
```
// DubboServicesMetadataEndpoint.java
/**
* Dubbo {@link Service} Metadata {@link Endpoint}
*/
@Endpoint(id = "dubboservices")
public class DubboServicesMetadataEndpoint extends AbstractDubboEndpoint {
@ReadOperation
public Map> services() {
// 获得所有的 ServiceBean
Map serviceBeansMap = super.getServiceBeansMap();
// 创建 Map
// KEY:Bean 的名字
// VALUE:Bean 的元数据
Map> servicesMetadata = new LinkedHashMap<>(serviceBeansMap.size());
// 遍历 serviceBeansMap 元素
for (Map.Entry entry : serviceBeansMap.entrySet()) {
// 获得 Bean 的名字
String serviceBeanName = entry.getKey();
// 获得 ServiceBean 对象
ServiceBean serviceBean = entry.getValue();
// 获得 Bean 的元数据
Map serviceBeanMetadata = super.resolveBeanMetadata(serviceBean);
// 获得 Service 对象。若获得到,则添加到 serviceBeanMetadata 中
Object service = resolveServiceBean(serviceBeanName, serviceBean);
if (service != null) {
// Add Service implementation class
serviceBeanMetadata.put("serviceClass", service.getClass().getName());
}
// 添加到 servicesMetadata 中
servicesMetadata.put(serviceBeanName, serviceBeanMetadata);
}
return servicesMetadata;
}
private Object resolveServiceBean(String serviceBeanName, ServiceBean serviceBean) {
int index = serviceBeanName.indexOf("#");
if (index > -1) {
Class> interfaceClass = serviceBean.getInterfaceClass();
String serviceName = serviceBeanName.substring(index + 1);
if (applicationContext.containsBean(serviceName)) {
return applicationContext.getBean(serviceName, interfaceClass);
}
}
return null;
}
}
```
### 5.10 DubboShutdownEndpoint
`com.alibaba.boot.dubbo.actuate.endpoint.DubboShutdownEndpoint` ,继承 AbstractDubboEndpoint 抽象类,关闭 Dubbo 。代码如下:
```
// DubboShutdownEndpoint.java
/**
* Dubbo Shutdown
*/
@Endpoint(id = "dubboshutdown")
public class DubboShutdownEndpoint extends AbstractDubboEndpoint {
@WriteOperation
public Map shutdown() throws Exception {
// 创建 Map
Map shutdownCountData = new LinkedHashMap<>();
// registries
// 获得注册的数量
int registriesCount = AbstractRegistryFactory.getRegistries().size();
// protocols
// 获得 Protocol 的数量
int protocolsCount = super.getProtocolConfigsBeanMap().size();
// 销毁 ProtocolConfig
ProtocolConfig.destroyAll();
// 添加到 shutdownCountData 中
shutdownCountData.put("registries", registriesCount);
shutdownCountData.put("protocols", protocolsCount);
// Service Beans
// 获得所有 ServiceBean ,然后逐个销毁
Map serviceBeansMap = super.getServiceBeansMap();
if (!serviceBeansMap.isEmpty()) {
for (ServiceBean serviceBean : serviceBeansMap.values()) {
serviceBean.destroy();
}
}
// 添加到 shutdownCountData 中
shutdownCountData.put("services", serviceBeansMap.size());
// Reference Beans
// 获得 ReferenceAnnotationBeanPostProcessor 对象
ReferenceAnnotationBeanPostProcessor beanPostProcessor = super.getReferenceAnnotationBeanPostProcessor();
// 获得 Reference Bean 的数量
int referencesCount = beanPostProcessor.getReferenceBeans().size();
// 销毁所有 Reference Bean
beanPostProcessor.destroy();
// 添加到 shutdownCountData 中
shutdownCountData.put("references", referencesCount);
// Set Result to complete
Map shutdownData = new TreeMap<>();
shutdownData.put("shutdown.count", shutdownCountData);
return shutdownData;
}
}
```
- 通过调用该接口,我们就可以远程关闭 Dubbo 服务。