社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
spring boot 中有个很诱人的组件–actuator,可以对spring boot应用做监控,只需在pom文件中加入如下配置即可:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
此时我们便可以访问/env,获得spring boot 应用的环境变量.关于如何使用,可以参考如下链接:
Spring Boot Actuator监控端点小结
actuator项目如图:
actuator中的端点类型有3种:
我们这里详细说明org.springframework.boot.actuate.endpoint中的实现.通过如下几点来进行分析:
在org.springframework.boot.actuate.endpoint 中还有2个子包-jmx(可通过jmx协议访问),mvc(通过spring mvc 暴露,可通过接口进行访问,在下篇文章进行分析).这里我们不关注这些,这看org.springframework.boot.actuate.endpoint 包下的类,类比较多,先看个类图吧,如下:
Endpoint接口–>一个端点可以用于暴露操作的实用信息.通常暴露信息是通过spring mvc 但是其他一些技术也能实现,可以通过继承AbstractEndpoint的方式实现自己的endpoint.代码如下:
public interface Endpoint<T> {
// 端点的逻辑标识(字母、数字和下划线('_') 组成)
String getId();
// 端点是否启用
boolean isEnabled();
// 端点是否输出敏感数据
boolean isSensitive();
// 调用端点,并返回调用结果
T invoke();
}
其中泛型参数T为暴露的数据类型.方法的作用已经注释,就不再赘述了.
Endpoint有一个子类–> AbstractEndpoint(Endpoint接口实现的抽象基类),该类实现了EnvironmentAware,因此, AbstractEndpoint也就持有了Environment.
AbstractEndpoint 有如下属性:
// 匹配包括下划线的任何单词字符。类似但不等价于“[A-Za-z0-9_]”
private static final Pattern ID_PATTERN = Pattern.compile("\w+");
// 通过EnvironmentAware接口注入
private Environment environment;
// 端点标识符
private String id;
// 是否默认敏感
private final boolean sensitiveDefault;
// 标识该端点是否暴露敏感信息
private Boolean sensitive;
// 是否端点可用
private Boolean enabled;
AbstractEndpoint方法实现了Endpoint接口中的getId, isEnabled, isSensitive,其中, getId只需返回AbstractEndpoint中的id属性即可,我们分别来看下其他方法的实现:
isEnabled,代码如下:
public boolean isEnabled() {
return EndpointProperties.isEnabled(this.environment, this.enabled);
}
调用
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;
}
3件事:
isSensitive和isEnabled实现差不多,如下:
public boolean isSensitive() {
return EndpointProperties.isSensitive(this.environment, this.sensitive,
this.sensitiveDefault);
}
调用:
public static boolean isSensitive(Environment environment, Boolean sensitive,boolean sensitiveDefault) {
if (sensitive != null) {
return sensitive;
}
if (environment != null
&& environment.containsProperty(ENDPOINTS_SENSITIVE_PROPERTY)) {
return environment.getProperty(ENDPOINTS_SENSITIVE_PROPERTY, Boolean.class);
}
return sensitiveDefault;
}
EnvironmentEndpoint–>暴露ConfigurableEnvironment 中的信息,继承了AbstractEndpoint.其默认构造器如下:
public EnvironmentEndpoint() {
// 设置 id 为 env
super("env");
}
调用
public AbstractEndpoint(String id) {
this(id, true);
}
最终,设置id为env,标识为敏感数据
其实现了invoke方法,代码如下:
public Map<String, Object> invoke() {
// 1. 返回值
Map<String, Object> result = new LinkedHashMap<String, Object>();
// 2. 将spring boot 中激活的profile 放入result中,key --> profile
result.put("profiles", getEnvironment().getActiveProfiles());
// 3. 获得PlaceholderSanitizingPropertyResolver --> 处理占位符,处理敏感数据
PropertyResolver resolver = getResolver();
// 4. 遍历environment 配置的PropertySource,依次处理之
for (Entry<String, PropertySource<?>> entry : getPropertySourcesAsMap()
.entrySet()) {
PropertySource<?> source = entry.getValue();
String sourceName = entry.getKey();
if (source instanceof EnumerablePropertySource) {
// 4.1 只针对EnumerablePropertySource 类型的PropertySource 进行处理--> 依次将属性添加到properties中,
// 如果属性值为string,则在添加前进行占位符,数据脱敏的处理
EnumerablePropertySource<?> enumerable = (EnumerablePropertySource<?>) source;
Map<String, Object> properties = new LinkedHashMap<String, Object>();
for (String name : enumerable.getPropertyNames()) {
Object property = source.getProperty(name);
Object resolved = property instanceof String
? resolver.resolvePlaceholders((String) property) : property;
properties.put(name, sanitize(name, resolved));
}
// 4.2 后置处理,该方法的实现是直接返回原始值,可以通过覆写的方式进行扩展
properties = postProcessSourceProperties(sourceName, properties);
if (properties != null) {
// 4.3 如果不为空,则添加到result中
result.put(sourceName, properties);
}
}
}
return result;
}
获得PlaceholderSanitizingPropertyResolver –> 处理占位符,处理敏感数据.代码如下:
public PropertyResolver getResolver() {
// 1. 实例化PlaceholderSanitizingPropertyResolver --> 处理占位符,处理敏感数据
PlaceholderSanitizingPropertyResolver resolver = new PlaceholderSanitizingPropertyResolver(
getPropertySources(), this.sanitizer);
// 2. 设置ignoreUnresolvableNestedPlaceholders 为true
resolver.setIgnoreUnresolvableNestedPlaceholders(true);
return resolver;
}
PlaceholderSanitizingPropertyResolver继承了PropertySourcesPropertyResolver,这样就能对占位符进行处理了,又因为其内部持有Sanitizer(用于敏感数据脱敏),复写了getPropertyAsRawString,这样就能处理占位符,敏感数据了.代码如下:
protected String getPropertyAsRawString(String key) {
String value = super.getPropertyAsRawString(key);
return (String) this.sanitizer.sanitize(key, value);
}
遍历environment 配置的PropertySource,依次处理之
属性配置,由于EnvironmentEndpoint被@ConfigurationProperties(prefix = “endpoints.env”)注解,因此可通过如下配置进行个性化配置:
endpoints.env.id=env
endpoints.env.sensitive=true
endpoints.env.enabled=true
同时,又因为其声明了如下方法:
public void setKeysToSanitize(String... keysToSanitize) {
this.sanitizer.setKeysToSanitize(keysToSanitize);
}
因此可以通过endpoints.env.keys-to-sanitize=xx,xx 来配置对指定的数据进行脱敏
自动化装配:
EnvironmentEndpoint的自动化装配是在EndpointAutoConfiguration中,代码如下:
@Bean
@ConditionalOnMissingBean
public EnvironmentEndpoint environmentEndpoint() {
return new EnvironmentEndpoint();
}
InfoEndpoint–>暴露应用信息,继承自AbstractEndpoint.
其字段和构造器如下:
private final List<InfoContributor> infoContributors;
public InfoEndpoint(List<InfoContributor> infoContributors) {
super("info", false);
Assert.notNull(infoContributors, "Info contributors must not be null");
this.infoContributors = infoContributors;
}
其内部持有了BeanFactory中所有InfoContributor类型的bean,其通过构造器注入
invoke 实现如下:
public Map<String, Object> invoke() {
Info.Builder builder = new Info.Builder();
for (InfoContributor contributor : this.infoContributors) {
contributor.contribute(builder);
}
Info build = builder.build();
return build.getDetails();
}
通过遍历其内部的持有infoContributors,因此调用其contribute将info的数据添加到Info.Builder中,最后通过Info.Builder构建出Info,返回Info持有的details(建造者模式). Info中的details为Map. 这里的代码比较简单,读者可自行阅读.
InfoContributor接口用于向Info$Builder添加信息,关于这部分的内容,我们后续文章有分析.这里就不在赘述了.
属性配置:
InfoEndpoint由于有@ConfigurationProperties(prefix = “endpoints.info”)注解,因此可通过如下进行配置:
endpoints.info.id=info
endpoints.info.sensitive=true
endpoints.info.enabled=true
自动化装配–>在EndpointAutoConfiguration中,代码如下:
@Bean
@ConditionalOnMissingBean
public InfoEndpoint infoEndpoint() throws Exception {
return new InfoEndpoint(this.infoContributors == null
? Collections.<InfoContributor>emptyList() : this.infoContributors);
}
RequestMappingEndpoint 实现了ApplicationContextAware接口,因此,在初始化该类时会注入applicationContext.
RequestMappingEndpoint,构造器如下:
public RequestMappingEndpoint() {
super("mappings");
}
因此, RequestMappingEndpoint的id为 mappings,默认为敏感
invoke 实现如下:
public Map<String, Object> invoke() {
Map<String, Object> result = new LinkedHashMap<String, Object>();
// 1. 从handlerMappings中获取HandlerMapping,默认情况下handlerMappings是不存在数据的
extractHandlerMappings(this.handlerMappings, result);
// 2. 从applicationContext中获取AbstractUrlHandlerMapping类型的bean,依次将其注册的handler 添加进去.
extractHandlerMappings(this.applicationContext, result);
// 3. 从methodMappings中获取HandlerMapping,默认情况下methodMappings是不存在数据的
extractMethodMappings(this.methodMappings, result);
// 3. 从applicationContext中获取AbstractUrlHandlerMapping类型的bean,依次获得其持有的HandlerMethods,进行处理.
extractMethodMappings(this.applicationContext, result);
return result;
}
从applicationContext中获取AbstractUrlHandlerMapping类型的bean,依次将其注册的handler 添加进去.代码如下:
protected void extractHandlerMappings(ApplicationContext applicationContext,
Map<String, Object> result) {
if (applicationContext != null) {
Map<String, AbstractUrlHandlerMapping> mappings = applicationContext
.getBeansOfType(AbstractUrlHandlerMapping.class);
for (Entry<String, AbstractUrlHandlerMapping> mapping : mappings.entrySet()) {
Map<String, Object> handlers = getHandlerMap(mapping.getValue());
for (Entry<String, Object> handler : handlers.entrySet()) {
result.put(handler.getKey(),
Collections.singletonMap("bean", mapping.getKey()));
}
}
}
}
获得AbstractUrlHandlerMapping类型的bean,此时有4个:
依次遍历mappings:
从applicationContext中获取AbstractUrlHandlerMapping类型的bean,依次获得其持有的HandlerMethods,进行处理.代码如下:
protected void extractMethodMappings(ApplicationContext applicationContext,
Map<String, Object> result) {
if (applicationContext != null) {
for (Entry<String, AbstractHandlerMethodMapping> bean : applicationContext
.getBeansOfType(AbstractHandlerMethodMapping.class).entrySet()) {
@SuppressWarnings("unchecked")
Map<?, HandlerMethod> methods = bean.getValue().getHandlerMethods();
for (Entry<?, HandlerMethod> method : methods.entrySet()) {
Map<String, String> map = new LinkedHashMap<String, String>();
map.put("bean", bean.getKey());
map.put("method", method.getValue().toString());
result.put(method.getKey().toString(), map);
}
}
}
}
属性配置–>可通过如下属性配置(因为有@ConfigurationProperties(prefix = “endpoints.mappings”)注解):
endpoints.mappings.enabled= # Enable the endpoint.
endpoints.mappings.id= # Endpoint identifier.
endpoints.mappings.sensitive= # Mark if the endpoint exposes sensitive information.
自动装配–>在EndpointAutoConfiguration$RequestMappingEndpointConfiguration中:
代码如下:
@Configuration
@ConditionalOnClass(AbstractHandlerMethodMapping.class)
protected static class RequestMappingEndpointConfiguration {
@Bean
@ConditionalOnMissingBean
public RequestMappingEndpoint requestMappingEndpoint() {
RequestMappingEndpoint endpoint = new RequestMappingEndpoint();
return endpoint;
}
}
当满足如下条件时生效,注册1个id为requestMappingEndpoint,类型为RequestMappingEndpoint的bean:
invoke 实现:
@Override
public List<ThreadInfo> invoke() {
return Arrays
.asList(ManagementFactory.getThreadMXBean().dumpAllThreads(true, true));
}
调用了ThreadMXBean的dumpAllThreads来返回所有活动线程的线程信息,并带有堆栈跟踪和同步信息。 当此方法返回时,返回数组中包含的一些线程可能已经终止。其中两个参数指的意义如下:
可参考如下链接:
接口 ThreadMXBean 一个很好用的线程管理接口类
可通过如下属性进行配置(因为有@ConfigurationProperties(prefix = “endpoints.dump”)注解):
endpoints.dump.enabled= # Enable the endpoint.
endpoints.dump.id= # Endpoint identifier.
endpoints.dump.sensitive= # Mark if the endpoint exposes sensitive information.
自动化装配:
在EndpointAutoConfiguration中声明,代码如下:
@Bean
@ConditionalOnMissingBean
public DumpEndpoint dumpEndpoint() {
return new DumpEndpoint();
}
没有启用,列在这里是为了做个提示
ShutdownEndpoint 实现了ApplicationContextAware.其构造器如下:
public ShutdownEndpoint() {
super("shutdown", true, false);
}
调用:
public AbstractEndpoint(String id, boolean sensitive, boolean enabled) {
setId(id);
this.sensitiveDefault = sensitive;
this.enabled = enabled;
}
因此,其id为shutdown,默认敏感,默认不启用.
如何启用ShutdownEndpoint呢?
因为ShutdownEndpoint在类上声明了@ConfigurationProperties(prefix = “endpoints.shutdown”)的注解,因此可以通过endpoints.shutdown.enabled = true的方式来在ShutdownEndpoint 实例化后进行配置(在AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization方法中,会调用ConfigurationPropertiesBindingPostProcessor实现属性注入).关于这部分的原理我们在 spring boot 源码解析13-@ConfigurationProperties是如何生效的 中有详解.
invoke 实现:
public Map<String, Object> invoke() {
if (this.context == null) {
return NO_CONTEXT_MESSAGE;
}
try {
return SHUTDOWN_MESSAGE;
}
finally {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(500L);
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
ShutdownEndpoint.this.context.close();
}
});
thread.setContextClassLoader(getClass().getClassLoader());
thread.start();
}
}
属性配置(因为有@ConfigurationProperties(prefix = “endpoints.shutdown”)注解):
endpoints.shutdown.enabled= # Enable the endpoint.
endpoints.shutdown.id= # Endpoint identifier.
endpoints.shutdown.sensitive= # Mark if the endpoint exposes sensitive information.
自动装配:
在EndpointAutoConfiguration中配置,代码如下:
@Bean
@ConditionalOnMissingBean
public ShutdownEndpoint shutdownEndpoint() {
return new ShutdownEndpoint();
}
AutoConfigurationReportEndpoint 构造器如下:
public AutoConfigurationReportEndpoint() {
super("autoconfig");
}
因此其id为autoconfig,默认为敏感
其内部持有ConditionEvaluationReport,会在实例化的时候进行注入.
invoke 实现:
public Report invoke() {
return new Report(this.autoConfigurationReport);
}
Report类上声明了如下注解:
@JsonPropertyOrder({ "positiveMatches", "negativeMatches", "exclusions" })
@JsonInclude(Include.NON_EMPTY)
Report 有如下字段:
// 匹配的
private final MultiValueMap<String, MessageAndCondition> positiveMatches;
// 不匹配的
private final Map<String, MessageAndConditions> negativeMatches;
// 去除的
private final List<String> exclusions;
// 一般为null
private final Report parent;
其中MessageAndCondition封装了ConditionAndOutcome中的condition,message以进行更好的展示(json友好).其类上声明了如下注解:
@JsonPropertyOrder({ "condition", "message" })
因此在进行输出的时候,先输出condition,再输出message.
构造器如下:
public MessageAndCondition(ConditionAndOutcome conditionAndOutcome) {
Condition condition = conditionAndOutcome.getCondition();
ConditionOutcome outcome = conditionAndOutcome.getOutcome();
this.condition = ClassUtils.getShortName(condition.getClass());
if (StringUtils.hasLength(outcome.getMessage())) {
this.message = outcome.getMessage();
}
else {
this.message = (outcome.isMatch() ? "matched" : "did not match");
}
}
构造器如下:
public Report(ConditionEvaluationReport report) {
this.positiveMatches = new LinkedMultiValueMap<String, MessageAndCondition>();
this.negativeMatches = new LinkedHashMap<String, MessageAndConditions>();
// 1. 通过report#getExclusions 获得不进行加载的bean
this.exclusions = report.getExclusions();
// 2.
for (Map.Entry<String, ConditionAndOutcomes> entry : report
.getConditionAndOutcomesBySource().entrySet()) {
// 2.1 如果该配置生效条件都匹配,则加入到positiveMatches,否则,加入到negativeMatches
if (entry.getValue().isFullMatch()) {
add(this.positiveMatches, entry.getKey(), entry.getValue());
}
else {
add(this.negativeMatches, entry.getKey(), entry.getValue());
}
}
// 3. 如果report存在父report,则进行初始化Report 赋值为当前类的parent 属性
boolean hasParent = report.getParent() != null;
this.parent = (hasParent ? new Report(report.getParent()) : null);
}
依次遍历第2步的返回值–>如果该配置生效条件都匹配,则加入到positiveMatches,否则,加入到negativeMatches.其中add 代码如下:
private void add(MultiValueMap<String, MessageAndCondition> map, String source,
ConditionAndOutcomes conditionAndOutcomes) {
String name = ClassUtils.getShortName(source);
for (ConditionAndOutcome conditionAndOutcome : conditionAndOutcomes) {
map.add(name, new MessageAndCondition(conditionAndOutcome));
}
}
因此positiveMatches,negativeMatches 中的key为配置类的简单类名.
属性配置(因为有@ConfigurationProperties(prefix = “endpoints.autoconfig”) 注解):
endpoints.autoconfig.enabled= # Enable the endpoint.
endpoints.autoconfig.id= # Endpoint identifier.
endpoints.autoconfig.sensitive= # Mark if the endpoint exposes sensitive information.
自动装配:
同样还是在EndpointAutoConfiguration中,代码如下:
@Bean
@ConditionalOnBean(ConditionEvaluationReport.class)
@ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
public AutoConfigurationReportEndpoint autoConfigurationReportEndpoint() {
return new AutoConfigurationReportEndpoint();
}
AutoConfigurationReportEndpoint 是通过ConditionEvaluationReport 来进行暴露信息.
ConditionEvaluationReport 字段如下:
private static final String BEAN_NAME = "autoConfigurationReport";
// 如果一个配置类中内部配置类不匹配,则在其外部类的所对应的ConditionAndOutcomes中添加1个AncestorsMatchedCondition
private static final AncestorsMatchedCondition ANCESTOR_CONDITION = new AncestorsMatchedCondition();
// key-->配置类类名,ConditionAndOutcomes-->匹配条件结果的封装
private final SortedMap<String, ConditionAndOutcomes> outcomes = new TreeMap<String, ConditionAndOutcomes>();
// 是否添加AncestorsMatchedCondition,默认为false
private boolean addedAncestorOutcomes;
// 父ConditionEvaluationReport,一般为null
private ConditionEvaluationReport parent;
// 去除加载的配置
private List<String> exclusions = Collections.emptyList();
// 在ConditionEvaluationReportAutoConfigurationImportListener#onAutoConfigurationImportEvent 添加,用于保存还没有
// 执行判断的class
private Set<String> unconditionalClasses = new HashSet<String>();
ConditionEvaluationReport 实例化过程如下:
在SpringApplication#run中会调用AbstractApplicationContext#refresh,在refresh会调用PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors,在该方法中最终会调用到AutoConfigurationImportSelector#selectImports方法.在该方法中会调用fireAutoConfigurationImportEvents,代码如下:
private void fireAutoConfigurationImportEvents(List<String> configurations,
Set<String> exclusions) {
List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
if (!listeners.isEmpty()) {
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this,
configurations, exclusions);
for (AutoConfigurationImportListener listener : listeners) {
invokeAwareMethods(listener);
listener.onAutoConfigurationImportEvent(event);
}
}
}
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
因此此处会调用ConditionEvaluationReportAutoConfigurationImportListener#onAutoConfigurationImportEvent.代码如下:
public void onAutoConfigurationImportEvent(AutoConfigurationImportEvent event) {
if (this.beanFactory != null) {
ConditionEvaluationReport report = ConditionEvaluationReport
.get(this.beanFactory);
report.recordEvaluationCandidates(event.getCandidateConfigurations());
report.recordExclusions(event.getExclusions());
}
}
实例化ConditionEvaluationReport,代码如下:
public static ConditionEvaluationReport get(
ConfigurableListableBeanFactory beanFactory) {
synchronized (beanFactory) {
ConditionEvaluationReport report;
// 1. 如果当前beanFactory包含autoConfigurationReport定义的话,就从beanFactory中获取,
if (beanFactory.containsSingleton(BEAN_NAME)) {
report = beanFactory.getBean(BEAN_NAME, ConditionEvaluationReport.class);
}
else {
// 否则就实例化一个,然后进行注册
report = new ConditionEvaluationReport();
beanFactory.registerSingleton(BEAN_NAME, report);
}
// 2. 如果存在父容器的话,就从父容器中获取。
locateParent(beanFactory.getParentBeanFactory(), report);
return report;
}
}
如果存在父容器的话,就从父容器中获取,并将其赋值为当前context中获得的ConditionEvaluationReport的父ConditionEvaluationReport.代码如下:
private static void locateParent(BeanFactory beanFactory,
ConditionEvaluationReport report) {
if (beanFactory != null && report.parent == null
&& beanFactory.containsBean(BEAN_NAME)) {
report.parent = beanFactory.getBean(BEAN_NAME,
ConditionEvaluationReport.class);
}
}
一般都是null,不会执行的
在AutoConfigurationReportEndpoint中是通过Report来进行暴露信息的,而在其构造器中,调用了ConditionEvaluationReport#getConditionAndOutcomesBySource方法,代码如下:
public Map<String, ConditionAndOutcomes> getConditionAndOutcomesBySource() {
if (!this.addedAncestorOutcomes) {
// 1. 如果addedAncestorOutcomes 设为false,则依次遍历outcomes,如果一个配置类中内部配置类不匹配,则在其外部类的所对应的ConditionAndOutcomes中添加1个AncestorsMatchedCondition
for (Map.Entry<String, ConditionAndOutcomes> entry : this.outcomes
.entrySet()) {
if (!entry.getValue().isFullMatch()) {
addNoMatchOutcomeToAncestors(entry.getKey());
}
}
this.addedAncestorOutcomes = true;
}
return Collections.unmodifiableMap(this.outcomes);
}
如果addedAncestorOutcomes 设为false,则依次遍历outcomes,如果一个配置类中内部配置类不匹配,则在其外部类的所对应的ConditionAndOutcomes中添加1个AncestorsMatchedCondition
返回outcomes.
问题来了, outcomes 中的数据是如何添加的?
答案: 有2处.
还是在AutoConfigurationImportSelector#selectImports中,会调用其filter方法.代码如下
private List<String> filter(List<String> configurations,
AutoConfigurationMetadata autoConfigurationMetadata) {
long startTime = System.nanoTime();
String[] candidates = configurations.toArray(new String[configurations.size()]);
boolean[] skip = new boolean[candidates.length];
boolean skipped = false;
// 1. 获取META-INFspring.factories/中配置的org.springframework.boot.autoconfigure.AutoConfigurationImportFilter,.OnClassCondition.依次进行遍历之
// 此时获得的是org.springframework.boot.autoconfigure.condition
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
// 1.1 进行属性注入
invokeAwareMethods(filter);
// 1.2 调用AutoConfigurationImportFilter#match 进行判断,依次遍历其返回值,如果返回的是false,则说明该配置为跳过,并将skipped设置为true
// 获得AutoConfigurationMetadata中配置的ConditionalOnClass,如果不会空,则依次遍历之,看是否在当前类路径下存在
// 如果不匹配的话,则调用ConditionEvaluationReport.#ecordConditionEvaluation 进行记录
// 由于此时AutoConfigurationMetadata 什么都没有配置,因此此步骤相当于空操作,最终会在第2步返回
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
if (!match[i]) {
skip[i] = true;
skipped = true;
}
}
}
// 2. 如果skipped 等于false,则直接返回configurations,说明没有配置是需要跳过的
if (!skipped) {
return configurations;
}
// 3. 依次遍历candidates,如果该配置是不进行跳过的,则添加至result中进行返回
List<String> result = new ArrayList<String>(candidates.length);
for (int i = 0; i < candidates.length; i++) {
if (!skip[i]) {
result.add(candidates[i]);
}
}
return new ArrayList<String>(result);
}
获取META-INFspring.factories/中配置的org.springframework.boot.autoconfigure.AutoConfigurationImportFilter,.OnClassCondition.依次进行遍历之.此时获得的是org.springframework.boot.autoconfigure.condition
如果skipped 等于false,则直接返回configurations,说明没有配置是需要跳过的
在ConfigurationClassParser#processConfigurationClass进行解析加载配置类时,会调用ConditionEvaluator#shouldSkip,在该方法中,会因此遍历配置类配置的@Conditional所对应的处理类.此时,如果处理类是SpringBootCondition的子类的话,就会调用ConditionEvaluationReport进行记录匹配结果. 代码如下:
private void recordEvaluation(ConditionContext context, String classOrMethodName,
ConditionOutcome outcome) {
if (context.getBeanFactory() != null) {
ConditionEvaluationReport.get(context.getBeanFactory())
.recordConditionEvaluation(classOrMethodName, this, outcome);
}
}
默认不生效,这里就不进行分析了
BeansEndpoint,构造器如下:
public BeansEndpoint() {
super("beans");
}
id为beans,默认为敏感
BeansEndpoint的字段如下:
// 继承自LiveBeansView,用于生成json格式的数据
private final HierarchyAwareLiveBeansView liveBeansView = new HierarchyAwareLiveBeansView();
// json 解析器对象
private final JsonParser parser = JsonParserFactory.getJsonParser();
由于BeansEndpoint实现了ApplicationContextAware接口,因此当前初始化时,会调用其setApplicationContext方法,代码如下:
public void setApplicationContext(ApplicationContext context) throws BeansException {
if (context.getEnvironment()
.getProperty(LiveBeansView.MBEAN_DOMAIN_PROPERTY_NAME) == null) {
this.liveBeansView.setLeafContext(context);
}
}
如果没有设置spring.liveBeansView.mbeanDomain的属性,则将HierarchyAwareLiveBeansView中的leafContext设置为传入的ApplicationContext(通常是当前应用所对应的上下文)
invoke实现如下:
public List<Object> invoke() {
return this.parser.parseList(this.liveBeansView.getSnapshotAsJson());
}
调用HierarchyAwareLiveBeansView#getSnapshotAsJson 生成json串.代码如下:
public String getSnapshotAsJson() {
if (this.leafContext == null) {
return super.getSnapshotAsJson();
}
// 2. 将leafContext的整个继承关系都添加到contexts中,即:如果给定的leafContext 存在父context,则一直递归的添加至contexts
// 直至顶级容器,然后调用LiveBeansView#generateJson 来生成json串
return generateJson(getContextHierarchy());
}
将leafContext的整个继承关系都添加到contexts中,即:如果给定的leafContext 存在父context,则一直递归的添加至contexts.代码如下:
private Set<ConfigurableApplicationContext> getContextHierarchy() {
Set<ConfigurableApplicationContext> contexts = new LinkedHashSet<ConfigurableApplicationContext>();
ApplicationContext context =
版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_26000415/article/details/79060258
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!