Spring Boot和多个外部配置文件

nir*_*nir 110 java spring config spring-boot

我有多个属性文件,我想从类路径加载.有一个默认设置/src/main/resources属于myapp.jar.我springcontext希望文件在类路径上.即

<util:properties id="Job1Props"
    location="classpath:job1.properties"></util:properties>

<util:properties id="Job2Props"
    location="classpath:job2.properties"></util:properties>
Run Code Online (Sandbox Code Playgroud)

我还需要使用外部集覆盖这些属性的选项.我有一个外部配置文件夹cwd.根据spring boot doc配置文件夹应该在classpath上.但是从doc中不清楚它是否只会覆盖applicaiton.propertiesconfig中的那个或所有属性.

当我测试它时,只会application.properties被拾取,其余的属性仍然从中获取/src/main/resources.我已经尝试将它们作为逗号分隔列表提供给spring.config.location但是默认设置仍然没有被覆盖.

如何使多个外部配置文件覆盖默认配置文件?

作为我目前使用的解决方法app.config.location(app特定属性),我通过命令行提供.即

java -jar myapp.jar app.config.location=file:./config
Run Code Online (Sandbox Code Playgroud)

然后我改变applicationcontext

<util:properties id="Job2Props"
    location="{app.config.location}/job2.properties"></util:properties>
Run Code Online (Sandbox Code Playgroud)

这就是我在加载Application时在文件和类路径之间进行分离的方法.
EDITS:

//psuedo code

if (StringUtils.isBlank(app.config.location)) {
            System.setProperty(APP_CONFIG_LOCATION, "classpath:");
}
Run Code Online (Sandbox Code Playgroud)

我真的不想使用上面的解决方法,并让spring覆盖类路径上的所有外部配置文件,就像它对application.properties文件所做的那样.

M. *_*num 136

使用Spring Boot时,属性按以下顺序加载(请参阅Spring Boot参考指南中的Externalized Configuration).

  1. 命令行参数.
  2. Java系统属性(System.getProperties()).
  3. OS环境变量.
  4. 来自java:comp/env的JNDI属性
  5. RandomValuePropertySource,只具有随机属性.*.
  6. 打包jar之外的应用程序属性(application.properties包括YAML和配置文件变体).
  7. 打包在jar中的应用程序属性(application.properties包括YAML和配置文件变体).
  8. @Configuration类上的@PropertySource注释.
  9. 默认属性(使用SpringApplication.setDefaultProperties指定).

解析属性时(即@Value("${myprop}")以相反的顺序进行解析(从9开始).

要添加不同的文件,您可以使用spring.config.location以逗号分隔的属性文件列表或文件位置(目录)的属性.

-Dspring.config.location=your/config/dir/
Run Code Online (Sandbox Code Playgroud)

上面的那个将添加一个目录,该目录将参考application.properties文件.

-Dspring.config.location=classpath:job1.properties,classpath:job2.properties
Run Code Online (Sandbox Code Playgroud)

这会将2个属性文件添加到已加载的文件中.

默认配置文件和位置在附加指定spring.config.location的文件和位置之前加载,这意味着后者将始终覆盖先前设置的属性.(另请参阅"Spring Boot参考指南"的本节).

如果spring.config.location包含目录(而不是文件),它们应该以/结尾(并且将附加spring.config.name在加载之前生成的名称).classpath:,classpath:/config,file:,file:config/始终使用默认搜索路径,而不考虑其值spring.config.location.通过这种方式,您可以为应用程序设置默认值application.properties(或者您选择的任何其他基本名称spring.config.name),并在运行时使用不同的文件覆盖它,保留默认值.

更新:由于spring.config.location的行为现在覆盖默认值而不是添加到默认值.您需要使用spring.config.additional-location来保留默认值.这是从1.x到2.x的行为变化

  • 这应该更新,因为`spring.config.location`的行为现在会覆盖默认值而不是添加到默认值.你需要使用`spring.config.additional-location`来保持默认值.这是从1.x到2.x的行为变化. (9认同)
  • 谢谢,但我已经阅读了这个参考文档,以下对我来说很困惑"-Dspring.config.location = your/config/dir /上面的一个将添加一个目录,将为application.properties文件提供参考." application.properties文件是什么意思.那只是一个档案.在任何情况下,如果它能够在末尾选择带有"/"的整个目录,那么我不需要将每个目录指定为以逗号分隔的列表.我想我已经尝试了两种方法,就像我在帖子中提到的那样,但我会再试一次 (2认同)
  • 引用文档没有帮助.如果文件清楚(足够?以特别需要的方式?)那么问题就没有必要了.例如,在这种情况下,我们真的不清楚`config.location`和`config.names`是如何交互的,尽管对于已经知道它们如何交互的人来说似乎很清楚.你能否在文档中更新*add*内容的答案? (2认同)

小智 31

使用Spring启动,spring.config.location确实有效,只提供逗号分隔的属性文件.

看下面的代码

@PropertySource(ignoreResourceNotFound=true,value="classpath:jdbc-${spring.profiles.active}.properties")
public class DBConfig{

     @Value("${jdbc.host}")
        private String jdbcHostName;
     }
}
Run Code Online (Sandbox Code Playgroud)

可以将jdbc.properties的默认版本放在应用程序中.可以设置外部版本.

java -jar target/myapp.jar --spring.config.location=classpath:file:///C:/Apps/springtest/jdbc.properties,classpath:file:///C:/Apps/springtest/jdbc-dev.properties
Run Code Online (Sandbox Code Playgroud)

根据使用spring.profiles.active属性设置的配置文件值,将获取jdbc.host的值.所以当(在Windows上)

set spring.profiles.active=dev
Run Code Online (Sandbox Code Playgroud)

jdbc.host将从jdbc-dev.properties获取值.

对于

set spring.profiles.active=default
Run Code Online (Sandbox Code Playgroud)

jdbc.host将从jdbc.properties获取值.


小智 22

看一下PropertyPlaceholderConfigurer,我发现使用它比注释更清晰.

例如

@Configuration
public class PropertiesConfiguration {


    @Bean
    public PropertyPlaceholderConfigurer properties() {
        final PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
//        ppc.setIgnoreUnresolvablePlaceholders(true);
        ppc.setIgnoreResourceNotFound(true);

        final List<Resource> resourceLst = new ArrayList<Resource>();

        resourceLst.add(new ClassPathResource("myapp_base.properties"));
        resourceLst.add(new FileSystemResource("/etc/myapp/overriding.propertie"));
        resourceLst.add(new ClassPathResource("myapp_test.properties"));
        resourceLst.add(new ClassPathResource("myapp_developer_overrides.properties")); // for Developer debugging.

        ppc.setLocations(resourceLst.toArray(new Resource[]{}));

        return ppc;
    }
Run Code Online (Sandbox Code Playgroud)


dav*_*xxx 18

Spring引导1.X和Spring Boot 2.X不提供相同的选项和行为Externalized Configuration.

M. Deinum的非常好的答案是指Spring Boot 1的特殊性.
我将在这里更新Spring Boot 2.

环境属性来源和订单

Spring Boot 2使用一种非常特殊的PropertySource顺序,旨在允许合理地覆盖值.按以下顺序考虑属性:

  • 在您的主目录上开发全局设置属性(当devtools处于活动状态时,〜/ .spring-boot-devtools.properties).

  • @TestPropertySource 你的测试注释.

  • @SpringBootTest#properties测试中的注释属性.命令行参数.

  • 来自SPRING_APPLICATION_JSON(嵌入在环境变量或系统属性中的内联JSON)的属性.

  • ServletConfig init参数.

  • ServletContext init参数.

  • JNDI属性来自java:comp/env.

  • Java系统属性(System.getProperties()).

  • OS环境变量.

  • RandomValuePropertySource,只有在随机具有的特性.*.

  • 特定于配置文件的应用程序属性在打包的jar(application-{profile}.properties和YAML变体)之外.

  • 打包在jar中的特定于配置文件的应用程序属性(application-{profile}.properties以及YAML变体).

  • 打包jar之外的应用程序属性(application.properties以及YAML变体).

  • 打包在jar中的应用程序属性(application.properties和YAML变体).

  • @PropertySource@Configuration班上的注释.默认属性(由设置指定 SpringApplication.setDefaultProperties).

要指定外部属性文件,您应该对以下选项感兴趣:

  • 特定于配置文件的应用程序属性在打包的jar(application-{profile}.properties和YAML变体)之外.

  • 打包jar之外的应用程序属性(application.properties以及YAML变体).

  • @PropertySource@Configuration班上的注释.默认属性(由设置指定 SpringApplication.setDefaultProperties).

您只能使用这3个选项中的一个,或根据您的要求进行组合.
例如,对于非常简单的情况,仅使用特定于配置文件的属性就足够了,但在其他情况下,您可能希望同时使用特定于配置文件的属性,默认属性和@PropertySource.

application.properties文件的默认位置

关于application.properties文件(和变体),默认情况下,Spring按以下顺序加载它们并在环境中添加它们的属性:

  • 当前目录的A/config子目录

  • 当前目录

  • 一个classpath/config包

  • 类路径根

更高的优先级是如此字面意思:
classpath:/,classpath:/config/,file:./,file:./config/.

如何使用具有特定名称的属性文件?

默认位置并不总是足够:默认位置(如默认文件名(application.properties))可能不适合.此外,如在OP问题中,您可能需要指定除application.properties(和变体)之外的多个配置文件.
所以spring.config.name还不够.

在这种情况下,您应该使用spring.config.locationenvironment属性(它是以逗号分隔的目录位置或文件路径列表)提供显式位置.
关于文件名模式的自由是有利于目录列表上的文件路径列表.
比如这样做:

java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties
Run Code Online (Sandbox Code Playgroud)

这种方式最简单,只是指定文件夹,但它也是非常精细地指定我们的配置文件并清楚地记录有效使用的属性的方法.

spring.config.location现在替换默认位置而不是添加它们

使用Spring Boot 1,spring.config.location参数在Spring环境中添加指定的位置.
但是从Spring Boot 2开始,spring.config.location文档中所述,将Spring使用的默认位置替换为Spring环境中的指定位置.

使用时配置自定义配置位置时 spring.config.location,它们会替换默认位置.例如,如果spring.config.location配置了的值 classpath:/custom-config/,file:./custom-config/,搜索顺序变成:

  1. file:./custom-config/

  2. classpath:custom-config/

spring.config.location现在是一种确保application.properties必须明确指定任何文件的方法.
对于不应该打包application.properties文件的超级JAR ,这是相当不错的.

为了保持spring.config.location使用Spring Boot 2时的旧行为,您可以使用新spring.config.additional-location属性而不是spring.config.location仍然添加文档所述的位置:

或者,当使用配置自定义配置位置时spring.config.additional-location,除默认位置外,还会使用 它们.


在实践中

因此,假设在OP问题中,您有2个外部属性文件要指定,并且1个属性文件包含在超级jar中.

要仅使用您指定的配置文件:

-Dspring.config.location=classpath:/job1.properties,classpath:/job2.properties,classpath:/applications.properties   
Run Code Online (Sandbox Code Playgroud)

要在默认位置向这些文件添加配置文件:

-Dspring.config.additional-location=classpath:/job1.properties,classpath:/job2.properties
Run Code Online (Sandbox Code Playgroud)

classpath:/applications.properties 在最后一个示例中不是必需的,因为默认位置具有该默认位置,并且此处的默认位置未被覆盖但是已扩展.


mxs*_*xsb 7

我有同样的问题.我希望能够在启动时使用外部文件覆盖内部配置文件,类似于Spring Boot application.properties检测.在我的例子中,它是一个user.properties文件,其中存储了我的应用程序用户.

我的要求:

从以下位置加载文件(按此顺序)

  1. 类路径
  2. 当前目录的A / config子目录.
  3. 当前目录
  4. 从目录或启动时命令行参数给出的文件位置

我提出了以下解决方案:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.PathResource;
import org.springframework.core.io.Resource;

import java.io.IOException;
import java.util.Properties;

import static java.util.Arrays.stream;

@Configuration
public class PropertiesConfig {

    private static final Logger LOG = LoggerFactory.getLogger(PropertiesConfig.class);

    private final static String PROPERTIES_FILENAME = "user.properties";

    @Value("${properties.location:}")
    private String propertiesLocation;

    @Bean
    Properties userProperties() throws IOException {
        final Resource[] possiblePropertiesResources = {
                new ClassPathResource(PROPERTIES_FILENAME),
                new PathResource("config/" + PROPERTIES_FILENAME),
                new PathResource(PROPERTIES_FILENAME),
                new PathResource(getCustomPath())
        };
        // Find the last existing properties location to emulate spring boot application.properties discovery
        final Resource propertiesResource = stream(possiblePropertiesResources)
                .filter(Resource::exists)
                .reduce((previous, current) -> current)
                .get();
        final Properties userProperties = new Properties();

        userProperties.load(propertiesResource.getInputStream());

        LOG.info("Using {} as user resource", propertiesResource);

        return userProperties;
    }

    private String getCustomPath() {
        return propertiesLocation.endsWith(".properties") ? propertiesLocation : propertiesLocation + PROPERTIES_FILENAME;
    }

}
Run Code Online (Sandbox Code Playgroud)

现在,应用程序使用类路径资源,但也检查其他给定位置的资源.将挑选和使用存在的最后一个资源.我可以使用java -jar myapp.jar --properties.location =/directory/myproperties.properties启动我的应用程序,以使用浮动我的船的属性位置.

这里的一个重要细节:使用空字符串作为@Value注释中properties.location的默认值,以避免在未设置属性时出错.

properties.location的约定是:使用属性文件的目录或路径作为properties.location.

如果要仅覆盖特定属性,则可以将具有setIgnoreResourceNotFound(true)的PropertiesFactoryBean与资源数组设置为位置一起使用.

我确信这个解决方案可以扩展到处理多个文件......

编辑

这里我的多个文件的解决方案:)像以前一样,这可以与一个PropertiesFactoryBean结合使用.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.PathResource;
import org.springframework.core.io.Resource;

import java.io.IOException;
import java.util.Map;
import java.util.Properties;

import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toMap;

@Configuration
class PropertiesConfig {

    private final static Logger LOG = LoggerFactory.getLogger(PropertiesConfig.class);
    private final static String[] PROPERTIES_FILENAMES = {"job1.properties", "job2.properties", "job3.properties"};

    @Value("${properties.location:}")
    private String propertiesLocation;

    @Bean
    Map<String, Properties> myProperties() {
        return stream(PROPERTIES_FILENAMES)
                .collect(toMap(filename -> filename, this::loadProperties));
    }

    private Properties loadProperties(final String filename) {
        final Resource[] possiblePropertiesResources = {
                new ClassPathResource(filename),
                new PathResource("config/" + filename),
                new PathResource(filename),
                new PathResource(getCustomPath(filename))
        };
        final Resource resource = stream(possiblePropertiesResources)
                .filter(Resource::exists)
                .reduce((previous, current) -> current)
                .get();
        final Properties properties = new Properties();

        try {
            properties.load(resource.getInputStream());
        } catch(final IOException exception) {
            throw new RuntimeException(exception);
        }

        LOG.info("Using {} as user resource", resource);

        return properties;
    }

    private String getCustomPath(final String filename) {
        return propertiesLocation.endsWith(".properties") ? propertiesLocation : propertiesLocation + filename;
    }

}
Run Code Online (Sandbox Code Playgroud)


Far*_*Skt 7

这是使用弹簧靴的一种简单方法

TestClass.java

@Configuration
@Profile("one")
@PropertySource("file:/{selected location}/app.properties")
public class TestClass {

    @Autowired
    Environment env;

    @Bean
    public boolean test() {
        System.out.println(env.getProperty("test.one"));
        return true;
    }
}
Run Code Online (Sandbox Code Playgroud)

app.properties背景下,您选择的位置

test.one = 1234
Run Code Online (Sandbox Code Playgroud)

你的春季启动应用

@SpringBootApplication

public class TestApplication {

    public static void main(String[] args) {
        SpringApplication.run(testApplication.class, args);
    }
}
Run Code Online (Sandbox Code Playgroud)

和预定义的application.properties上下文

spring.profiles.active = one
Run Code Online (Sandbox Code Playgroud)

您可以根据需要编写任意数量的配置类,并通过设置spring.profiles.active =配置文件名称/名称{以逗号分隔}来启用/禁用它们

你可以看到弹簧靴很棒,只需要一段时间熟悉,值得一提的是你也可以在你的领域使用@Value

@Value("${test.one}")
String str;
Run Code Online (Sandbox Code Playgroud)


小智 6

spring boot允许我们为不同的环境编写不同的配置文件,例如我们可以为生产,qa和本地环境提供单独的属性文件

application-local.properties文件,根据我的本地机器进行配置

spring.profiles.active=local

spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
spring.data.mongodb.database=users
spring.data.mongodb.username=humble_freak
spring.data.mongodb.password=freakone

spring.rabbitmq.host=localhost
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.port=5672

rabbitmq.publish=true
Run Code Online (Sandbox Code Playgroud)

同样,我们可以根据需要编写application-prod.properties和application-qa.properties多个属性文件

然后编写一些脚本来启动不同环境的应用程序,例如

mvn spring-boot:run -Drun.profiles=local
mvn spring-boot:run -Drun.profiles=qa
mvn spring-boot:run -Drun.profiles=prod
Run Code Online (Sandbox Code Playgroud)


rob*_*ins 5

我刚刚遇到了类似的问题并最终找出原因:application.properties文件具有错误的所有权和rwx属性.因此,当tomcat启动时,application.properties文件位于正确的位置,但由另一个用户拥有:

$ chmod 766 application.properties

$ chown tomcat application.properties
Run Code Online (Sandbox Code Playgroud)