Spring Boot和多个外部configuration文件
我有多个属性文件,我想从类path加载。 在/src/main/resources
下有一个默认设置,它是myapp.jar
一部分。 我的springcontext
期望文件在类path。 即
<util:properties id="Job1Props" location="classpath:job1.properties"></util:properties> <util:properties id="Job2Props" location="classpath:job2.properties"></util:properties>
我也需要用外部设置覆盖这些属性的选项。 我在cwd
有一个外部configuration文件夹。 按照Spring引导文档config文件夹应该在classpath上。 但是从doc不清楚它是否只会覆盖来自那里的applicaiton.properties
或者config中的所有属性。
当我testing它时,只有application.properties
被拾取,剩下的属性仍然从/src/main/resources
被拾取。 我已经尝试提供他们作为逗号分隔列表spring.config.location
但默认设置仍然没有被覆盖。
我如何使多个外部configuration文件覆盖默认的?
作为解决方法我目前使用app.config.location
(特定于应用程序的属性),我通过命令行提供。 即
java -jar myapp.jar app.config.location=file:./config
我改变了我的applicationcontext
上下文
<util:properties id="Job2Props" location="{app.config.location}/job2.properties"></util:properties>
这就是我加载应用程序时如何分离文件和类path。
EDITS:
//psuedo code if (StringUtils.isBlank(app.config.location)) { System.setProperty(APP_CONFIG_LOCATION, "classpath:"); }
我真的不想使用上面的解决方法,并popup覆盖类path上的所有外部configuration文件,就像它为application.properties
文件。
在使用Spring Boot时,按以下顺序加载属性(请参阅Spring Boot参考指南中的“ 外部化configuration ”)。
- 命令行参数。
- Java系统属性(System.getProperties())。
- OS环境variables。
- 来自java:comp / env的JNDI属性
- 一个RandomValuePropertySource只具有随机的属性*。
- 包装jar外的应用程序属性(application.properties,包括YAML和configuration文件变体)。
- 打包在jar中的应用程序属性(application.properties包括YAML和configuration文件变体)。
- @Configuration类的@PropertySource注释。
- 默认属性(使用SpringApplication.setDefaultProperties指定)。
当parsing属性(即@Value("${myprop}")
parsing是以相反的顺序(从9开始)完成的。
要添加不同的文件,您可以使用spring.config.location
属性,该属性使用以逗号分隔的属性文件或文件位置(目录)列表。
-Dspring.config.location=your/config/dir/
上面的那个将添加一个目录,这个目录将被用于application.properties
文件。
-Dspring.config.location=classpath:job1.properties,classpath:job2.properties
这会将2个属性文件添加到加载的文件中。
默认configuration文件和位置是在spring.config.location
指定的spring.config.location
之前加载的,意味着后者将始终覆盖之前设置的属性。 (另见“Spring Boot参考指南”的这一部分 )。
如果
spring.config.location
包含目录(而不是文件),它们应该以/结尾(并且在被加载之前会附加从spring.config.name
生成的名称)。 无论spring.config.location
的值如何,始终使用默认searchpathclasspath:,classpath:/config,file:,file:config/
。 通过这种方式,您可以在application.properties
(或其他您使用spring.config.name
select的基本名称)中为您的应用程序设置默认值,并在运行时使用不同的文件覆盖它,并保留默认值。
看看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; }
使用Spring引导,spring.config.location不起作用,只需提供逗号分隔的属性文件。
看下面的代码
@PropertySource(ignoreResourceNotFound=true,value="classpath:jdbc-${spring.profiles.active}.properties") public class DBConfig{ @Value("${jdbc.host}") private String jdbcHostName; } }
可以将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
根据使用spring.profiles.active属性设置的configuration文件值,jdbc.host的值将被拾取。 所以当(在Windows上)
set spring.profiles.active=dev
jdbc.host将从jdbc-dev.properties中获取值。
对于
set spring.profiles.active=default
jdbc.host将从jdbc.properties中获取值。
我刚刚遇到类似的问题,最后找出原因:application.properties文件的所有权和rwx属性都是错误的。 所以当tomcat启动时,application.properties文件位于正确的位置,但由另一个用户拥有:
$ chmod 766 application.properties $ chown tomcat application.properties
我有同样的问题。 我希望能够在启动时用外部文件覆盖内部configuration文件,类似于Spring Boot application.properties检测。 在我的情况下,这是一个user.properties文件,我的应用程序用户存储在那里。
我的要求:
从以下位置加载文件(按此顺序)
- 类path
- 当前目录的A / config子目录。
- 当前目录
- 从目录或启动时由命令行参数给出的文件位置
我想出了以下解决scheme:
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; } }
现在应用程序使用类path资源,但也检查其他给定位置的资源。 存在的最后一个资源将被挑选和使用。 我可以用java -jar myapp.jar –properties.location = / directory / myproperties.properties启动我的应用程序,以使用浮动我的船的属性位置。
这里有一个重要的细节:在@Value批注中,对于properties.location使用空string作为默认值,以避免在未设置属性时出现错误。
properties.location的约定是:使用属性文件的目录或path作为properties.location。
如果只想覆盖特定属性,则可以使用带有setIgnoreResourceNotFound(true)的PropertiesFactoryBean,并将资源数组设置为位置。
我敢肯定,这个解决scheme可以扩展到处理多个文件…
编辑
在这里,我的解决scheme为多个文件:)像以前一样,这可以结合一个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; } }
这是一个使用弹簧引导的简单方法
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; } }
app.properties上下文,在您select的位置
test.one = 1234
你的春季启动应用程序
@SpringBootApplication public class TestApplication { public static void main(String[] args) { SpringApplication.run(testApplication.class, args); } }
和预定义的application.properties上下文
spring.profiles.active = one
你可以根据自己的喜好编写任意多的configuration类,并通过设置spring.profiles.active =configuration文件名称/名称{以逗号分隔}来启用/禁用它们。
正如你所看到的,春季开机很棒,只需要一段时间才能熟悉,值得一提的是你也可以在你的领域使用@Value
@Value("${test.one}") String str;
弹簧启动允许我们编写不同的configuration文件来写入不同的环境,例如,我们可以为生产,qa和本地环境
application-local.properties文件的configuration根据我的本地机器是
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
同样,我们可以根据需要将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