在 Spring 中外部化配置 在同一个容器中使用多个应用程序 运行 启动
Externalizing configuration in Spring Boot with multiple applications running in the same container
我正在构建多个 Spring 引导应用程序,这些应用程序将部署在同一个 servlet 容器上。但是我很难 Spring 引导以我想要的方式使用外部配置文件,而不是像框架想要的那样。
情况:
- 多个 Spring 引导应用程序将部署在单个 servlet 容器(WAR 文件)
- 配置文件的位置将通过 JVM 设置 属性
spring.config.location
- 嵌入式部署不是一种选择
问题:
由于应用程序部署在同一个 JVM 上,因此 属性 spring.config.location
对所有应用程序具有相同的值。我希望我们的应用程序都使用相同的配置文件命名 (application.properties),因此指定 spring.config.name
不是一个选项。
我想要什么:
- 无需设置
spring.config.name
因为配置名称应该在我们所有的应用程序中标准化(常量)
- 外部配置属性应该覆盖 application.properties 中打包在我部署的 WAR
中的值
- 配置文件特定配置(应用程序-{profile})应该是可能的
- 代码中没有硬编码的配置位置
在每个应用程序目录布局中组织配置文件:
${spring.config.location}/app1/application.properties
${spring.config.location}/app2/application.properties
${spring.config.location}/app3/application.properties
问题:
是否有某种机制可以影响或覆盖外部配置文件的加载或解析?
还有其他方法可以达到预期的效果吗?
您可以使用 @PropertySource
实现您想要的效果。根据官方文档(Externalized Configuration),您可以使用此注释来外部化配置文件,例如:
@Configuration
@PropertySource("file:/path/to/application.properties")
public class AppConfig {
}
如 here 中所述,在 @PropertySource
中,您可以使用占位符,这些占位符将针对其他 属性 来源进行解析,例如application.properties
中声明的值
Assuming that "my.placeholder" is present in one of the property sources already registered, e.g. system properties or environment variables, the placeholder will be resolved to the corresponding value. If not, then "default/path" will be used as a default. Expressing a default value (delimited by colon ":") is optional. If no default is specified and a property cannot be resolved, an IllegalArgumentException will be thrown.
您可以将 properties_home
声明为环境变量,并在 application.properties
文件中声明 application_id
。
@Configuration
@PropertySource("${properties_home}/${application_id}/application.properties")
public class AppConfig {
}
不要忘记启用解析占位符的支持:
In order to resolve ${...} placeholders in bean definitions or @Value annotations using properties from a PropertySource, one must register a PropertySourcesPlaceholderConfigurer. This happens automatically when using in XML, but must be explicitly registered using a static @Bean method when using @Configuration classes.
更新:
为了覆盖外部文件的属性,您可以使用 spring 配置文件。在打包好的application.properties里面你需要设置:
spring.profiles.active=external
在位于 "${properties_home}/${application_id}/application.properties"
.
的文件中声明所有您希望优先作为外部配置文件一部分的属性
@Iulian Rosca 关于使用像 ${properties_home}/${application_id}/application.properties
这样的模式的建议让我想到定义一个像 app.config.root
这样的自定义 JVM 属性 并使用这个 属性在应用程序生命周期的早期覆盖 spring.config.location
。
我的应用程序 class 现在看起来像这样并且适用于嵌入式和容器部署:
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return configureApplication(builder);
}
public static void main(String[] args) {
configureApplication(new SpringApplicationBuilder()).run(args);
}
private static SpringApplicationBuilder configureApplication(SpringApplicationBuilder builder) {
return builder
.sources(Application.class)
.properties("spring.config.location:${${app.config.root}/myapp1/:#{null}}");
}
}
此解决方案的重要说明:
app.config.root
必须由 JVM 或 JNDI 在外部设置 属性
app.config.root
只能包含一个外部配置路径(对于我的要求,这就足够了)与 spring.config.location
相比,其中可以指定多个逗号分隔的路径
SpringApplicationBuilder.properties(...)
设置应用程序的默认属性。因此,无法再从外部指定 spring.config.location
,因为 JVM 或 JNDI 属性优先于默认属性,因此会再次覆盖 spring.config.location
。
我正在构建多个 Spring 引导应用程序,这些应用程序将部署在同一个 servlet 容器上。但是我很难 Spring 引导以我想要的方式使用外部配置文件,而不是像框架想要的那样。
情况:
- 多个 Spring 引导应用程序将部署在单个 servlet 容器(WAR 文件)
- 配置文件的位置将通过 JVM 设置 属性
spring.config.location
- 嵌入式部署不是一种选择
问题:
由于应用程序部署在同一个 JVM 上,因此 属性 spring.config.location
对所有应用程序具有相同的值。我希望我们的应用程序都使用相同的配置文件命名 (application.properties),因此指定 spring.config.name
不是一个选项。
我想要什么:
- 无需设置
spring.config.name
因为配置名称应该在我们所有的应用程序中标准化(常量) - 外部配置属性应该覆盖 application.properties 中打包在我部署的 WAR 中的值
- 配置文件特定配置(应用程序-{profile})应该是可能的
- 代码中没有硬编码的配置位置
在每个应用程序目录布局中组织配置文件:
${spring.config.location}/app1/application.properties ${spring.config.location}/app2/application.properties ${spring.config.location}/app3/application.properties
问题:
是否有某种机制可以影响或覆盖外部配置文件的加载或解析?
还有其他方法可以达到预期的效果吗?
您可以使用 @PropertySource
实现您想要的效果。根据官方文档(Externalized Configuration),您可以使用此注释来外部化配置文件,例如:
@Configuration
@PropertySource("file:/path/to/application.properties")
public class AppConfig {
}
如 here 中所述,在 @PropertySource
中,您可以使用占位符,这些占位符将针对其他 属性 来源进行解析,例如application.properties
Assuming that "my.placeholder" is present in one of the property sources already registered, e.g. system properties or environment variables, the placeholder will be resolved to the corresponding value. If not, then "default/path" will be used as a default. Expressing a default value (delimited by colon ":") is optional. If no default is specified and a property cannot be resolved, an IllegalArgumentException will be thrown.
您可以将 properties_home
声明为环境变量,并在 application.properties
文件中声明 application_id
。
@Configuration
@PropertySource("${properties_home}/${application_id}/application.properties")
public class AppConfig {
}
不要忘记启用解析占位符的支持:
In order to resolve ${...} placeholders in bean definitions or @Value annotations using properties from a PropertySource, one must register a PropertySourcesPlaceholderConfigurer. This happens automatically when using in XML, but must be explicitly registered using a static @Bean method when using @Configuration classes.
更新:
为了覆盖外部文件的属性,您可以使用 spring 配置文件。在打包好的application.properties里面你需要设置:
spring.profiles.active=external
在位于 "${properties_home}/${application_id}/application.properties"
.
@Iulian Rosca 关于使用像 ${properties_home}/${application_id}/application.properties
这样的模式的建议让我想到定义一个像 app.config.root
这样的自定义 JVM 属性 并使用这个 属性在应用程序生命周期的早期覆盖 spring.config.location
。
我的应用程序 class 现在看起来像这样并且适用于嵌入式和容器部署:
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return configureApplication(builder);
}
public static void main(String[] args) {
configureApplication(new SpringApplicationBuilder()).run(args);
}
private static SpringApplicationBuilder configureApplication(SpringApplicationBuilder builder) {
return builder
.sources(Application.class)
.properties("spring.config.location:${${app.config.root}/myapp1/:#{null}}");
}
}
此解决方案的重要说明:
app.config.root
必须由 JVM 或 JNDI 在外部设置 属性app.config.root
只能包含一个外部配置路径(对于我的要求,这就足够了)与spring.config.location
相比,其中可以指定多个逗号分隔的路径SpringApplicationBuilder.properties(...)
设置应用程序的默认属性。因此,无法再从外部指定spring.config.location
,因为 JVM 或 JNDI 属性优先于默认属性,因此会再次覆盖spring.config.location
。