Spring Boot配置文件如何覆盖Apollo

问题背景

在使用Spring Boot开发应用时,我们经常使用配置文件来配置应用的各项属性。而在实际的项目中,可能会使用Apollo作为配置中心来管理配置文件,以便动态地调整配置项。然而,当我们使用Apollo时,如何保证配置文件的优先级,使得我们可以方便地覆盖Apollo中的配置项,成为了一个需要解决的问题。

解决方案

为了解决这个问题,我们可以通过对Spring Boot的配置加载过程进行调整来实现。Spring Boot会按照固定的顺序加载配置文件,我们可以通过在应用启动时指定配置文件的加载顺序,来实现Apollo配置文件的覆盖。

配置文件加载顺序

Spring Boot默认的配置文件加载顺序为:application.properties -> application.yml -> bootstrap.properties -> bootstrap.yml。其中,bootstrap开头的配置文件优先级更高,会覆盖application开头的配置文件。

方案步骤

  1. 在项目的resources目录下创建bootstrap.propertiesbootstrap.yml文件,并将Apollo的配置项写入该文件中。这样做的目的是让Apollo的配置文件先加载,以便覆盖后续加载的配置文件。

  2. 在启动应用的时候,通过命令行参数或启动脚本指定spring.config.namespring.config.location的值,以指定应用的配置文件名和加载路径。

    • 通过命令行参数指定:java -jar myproject.jar --spring.config.name=myproject --spring.config.location=classpath:/config/

    • 通过启动脚本指定:java -jar myproject.jar -Dspring.config.name=myproject -Dspring.config.location=file:/opt/myproject/config/

    这样做可以确保在启动应用时,优先加载指定位置的配置文件,而不是默认的配置文件。

  3. 确保应用的配置文件中有spring.profiles.active属性,并设置为default。这样可以确保Apollo的配置项在其他Profile中被覆盖。

    spring:
      profiles:
        active: default
    
  4. 在应用的配置文件中,通过${}占位符来使用Apollo中的配置项。在Apollo中创建相应的命名空间和配置项,并为其设定合适的值。

    # application.yml
    server:
      port: ${server.port}
    

    这样,应用在启动时会先加载Apollo的配置文件,然后再加载应用的配置文件,其中的占位符会被Apollo的配置值替换掉。

  5. 启动应用,Apollo的配置项就会覆盖应用中相同的配置项。

类图

下面是一个简化的类图,展示了上述解决方案的类和关系。

classDiagram
    class ApolloConfig {
        +getProperty(key: String): String
    }

    class ConfigFile {
        +load(file: String): Properties
        +getProperty(key: String): String
    }

    class Application {
        +main(args: String[]): void
        -resolveConfigLocation(): String
    }

    class Bootstrap {
        +main(args: String[]): void
        -resolveConfigLocation(): String
    }

    Application --> ConfigFile
    Bootstrap --> ConfigFile
    Application --> ApolloConfig
    Bootstrap --> ApolloConfig

示例代码

下面是一个示例代码,演示了如何使用上述解决方案来实现Apollo配置文件的覆盖。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Configuration;

@Configuration
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        // 指定应用的配置文件名和加载路径
        System.setProperty("spring.config.name", "myproject");
        System.setProperty("spring.config.location", "classpath:/config/");

        // 设置默认Profile
        System.setProperty("spring.profiles.active", "default");

        SpringApplication.run(Application.class, args);
    }
}
# application.yml
server:
  port: ${server.port}
import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigService;

@Configuration
public class ApolloConfig {
    private static final String APOLLO_NAMESPACE = "myproject";

    public String getProperty