Let config client fail before bean creation (#1977)

If the config client fails and fail fast is enabled, the ConfigClientFailFastException will already be thrown when the bootstrap context is closed instead of during auto-configuration.
This commit is contained in:
Henning Pöttker
2023-02-10 15:12:23 +01:00
committed by GitHub
parent 648f4816ac
commit d6e84660fa
3 changed files with 20 additions and 30 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2019 the original author or authors.
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,7 +21,6 @@ import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.TypeReference;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@@ -29,11 +28,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.config.ConfigDataLocation;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.cloud.config.environment.PropertySource;
import org.springframework.cloud.context.refresh.ContextRefresher;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
@@ -95,23 +92,6 @@ public class ConfigClientAutoConfiguration {
}
@Configuration(proxyBeanMethods = false)
protected class ConfigClientFailFastListener implements ApplicationListener<ApplicationStartedEvent> {
@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
try {
ConfigClientFailFastException exception = event.getApplicationContext()
.getBean(ConfigClientFailFastException.class);
throw exception;
}
catch (NoSuchBeanDefinitionException e) {
// ignore
}
}
}
}
class ConfigClientHints implements RuntimeHintsRegistrar {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2020 the original author or authors.
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -93,9 +93,9 @@ public class ConfigServerConfigDataLoader implements ConfigDataLoader<ConfigServ
return interceptor.apply(new LoadContext(context, resource, binder, this::doLoad));
}
catch (ConfigClientFailFastException e) {
context.getBootstrapContext()
.addCloseListener(event -> event.getApplicationContext().getBeanFactory()
.registerSingleton(ConfigClientFailFastException.class.getSimpleName(), e));
context.getBootstrapContext().addCloseListener(event -> {
throw e;
});
return new ConfigData(Collections.emptyList());
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2018-2019 the original author or authors.
* Copyright 2018-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,9 +19,11 @@ package sample;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension;
import org.springframework.stereotype.Component;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -39,16 +41,24 @@ public class ApplicationFailFastTests {
}
@Test
public void configDataContextFails(CapturedOutput output) {
public void configDataContextFailsFast(CapturedOutput output) {
assertThatThrownBy(() -> {
new SpringApplicationBuilder().sources(Application.class).run("--server.port=0",
"--spring.cloud.config.enabled=true", "--spring.cloud.config.fail-fast=true",
new SpringApplicationBuilder().sources(Application.class, PropertyInjectionConfiguration.class).run(
"--server.port=0", "--spring.cloud.config.enabled=true", "--spring.cloud.config.fail-fast=true",
"--spring.config.import=optional:configserver:http://serverhostdoesnotexist:1234",
"--spring.cloud.config.server.enabled=false", "--logging.level.org.springframework.retry=TRACE",
"--logging.level.org.springframework.cloud.config=TRACE",
"--logging.level.org.springframework.boot.context.config=TRACE");
}).as("Exception not caused by fail fast").hasMessageContaining("fail fast");
assertThat(output).contains("Retry: count=5");
assertThat(output).contains("Retry: count=5").doesNotContain("Could not resolve placeholder");
}
@Component
private static class PropertyInjectionConfiguration {
@Value("${some.property}")
private String someProperty;
}
}