Spring代码配置spring.cloud.config.discovery不生效,求助无文件解决方案
这个问题的核心原因是Spring Cloud的Bootstrap上下文初始化优先级高于应用上下文,你通过SpringApplicationBuilder.properties()设置的配置是应用上下文层面的,但配置中心的发现逻辑(spring.cloud.config.discovery.*这类属性)是在Bootstrap阶段生效的——此时应用上下文的属性还没被加载,所以Bootstrap上下文会使用默认的配置中心地址http://localhost:8888。
要在不使用外部配置文件的前提下解决这个问题,你需要让Bootstrap上下文能读取到配置中心发现的相关属性,这里有两种可靠的方法:
方法一:提前设置系统属性
在构建SpringApplication之前,把Bootstrap阶段需要的属性设置为JVM系统属性,因为Bootstrap上下文会优先读取系统属性:
// 先设置Bootstrap阶段必需的配置中心发现属性 System.setProperty("spring.cloud.config.failFast", "true"); System.setProperty("spring.cloud.config.discovery.enabled", "true"); System.setProperty("spring.cloud.config.discovery.serviceId", "config"); System.setProperty("spring.profiles.active", "cloud"); // 这个也需要在Bootstrap阶段生效 // 再设置应用级别的属性 Map<String, Object> properties = new HashMap<>(); properties.put("server.address", "0.0.0.0"); properties.put("server.port", 8080); properties.put("spring.application.name", "someApp"); properties.put("eureka.instance.preferIpAddress", true); properties.put("eureka.instance.statusPageUrlPath", "/health"); // 构建并启动应用 new SpringApplicationBuilder() .bannerMode(Banner.Mode.OFF) .properties(properties) .sources(SpringConfiguration.class) .web(false) .registerShutdownHook(true) .build() .run();
方法二:自定义Environment并注入Bootstrap属性
这种方法更优雅,不会污染全局系统属性,你可以创建一个自定义的ConfigurableEnvironment,把Bootstrap属性添加到最优先的属性源中,再传递给SpringApplicationBuilder:
// 创建标准环境实例 StandardEnvironment environment = new StandardEnvironment(); // 定义Bootstrap阶段需要的属性 Map<String, Object> bootstrapProps = new HashMap<>(); bootstrapProps.put("spring.cloud.config.failFast", true); bootstrapProps.put("spring.cloud.config.discovery.enabled", true); bootstrapProps.put("spring.cloud.config.discovery.serviceId", "config"); bootstrapProps.put("spring.profiles.active", "cloud"); // 将自定义Bootstrap属性源添加到最前面,确保优先级最高 environment.getPropertySources().addFirst(new MapPropertySource("custom-bootstrap", bootstrapProps)); // 应用级属性 Map<String, Object> appProps = new HashMap<>(); appProps.put("server.address", "0.0.0.0"); appProps.put("server.port", 8080); appProps.put("spring.application.name", "someApp"); appProps.put("eureka.instance.preferIpAddress", true); appProps.put("eureka.instance.statusPageUrlPath", "/health"); // 构建应用时传入自定义环境 new SpringApplicationBuilder() .bannerMode(Banner.Mode.OFF) .environment(environment) .properties(appProps) .sources(SpringConfiguration.class) .web(false) .registerShutdownHook(true) .build() .run();
为什么这两种方法有效?
Bootstrap上下文是Spring Cloud用来加载外部配置中心配置的独立上下文,它会在应用上下文之前初始化。它的配置来源包括:系统属性、环境变量、bootstrap配置文件等,但不会直接读取SpringApplicationBuilder.properties()设置的属性——因为这些属性属于后续初始化的应用上下文。
通过上述方法把Bootstrap必需的属性放到系统属性或自定义Environment的高优先级属性源中,就能让Bootstrap上下文在初始化时读取到这些配置,从而通过Eureka发现配置中心的地址,而不是使用默认值。
另外,你通过环境变量传入的eureka_client_serviceUrl_defaultZone等配置,会被StandardEnvironment自动读取,所以不需要额外处理。
内容的提问来源于stack exchange,提问作者Bartek Andrzejczak

