java 如何使用 spring 加载基于服务器环境的属性文件,以便可以注入值?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/46081338/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to load a properties file based on the server environment with spring so that the values can be injected?
提问by Dan
To my surprise I have had a difficult time finding an answer to this question. I have Seen many examples where you can use @PropertySource to load a specific properties file for a class. I have also seen examples where you can easily add different property files in spring boot projects. But what I want to do is to do this for a spring project that is NOT spring boot and load a properties file so that the values of this file can be injected in classes annotated with @Component which is dependent on the server environment. So for example if I am on development server I want a particular properties file loaded and on production a different properties file. The reason that I am doing it like this is because my data and service layers are their own modules. These modules contain their own unit tests and can be imported as their own modules in other spring boot projects. I need properties files to be loaded to serve these modules which use spring but not spring boot. I have tried the following, but this does not work.
令我惊讶的是,我很难找到这个问题的答案。我已经看到许多示例,您可以使用 @PropertySource 加载类的特定属性文件。我还看到了一些示例,您可以在这些示例中轻松地在 Spring Boot 项目中添加不同的属性文件。但是我想要做的是为一个不是 spring boot 的 spring 项目执行此操作并加载一个属性文件,以便可以将此文件的值注入到用 @Component 注解的类中,该类取决于服务器环境。因此,例如,如果我在开发服务器上,我想要加载一个特定的属性文件,并在生产中使用不同的属性文件。我这样做的原因是因为我的数据和服务层是它们自己的模块。这些模块包含自己的单元测试,可以在其他 spring boot 项目中作为自己的模块导入。我需要加载属性文件来为这些使用 spring 但不使用 spring 引导的模块提供服务。我尝试了以下方法,但这不起作用。
@Configuration
@Profile("test")
@EnableJpaRepositories("com.hi.repository")
@EnableTransactionManagement
@EnableScheduling
public class InfrastructureConfig {
...
@Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
Map<String, String> env = System.getenv();
String propertiesFile=null;
String e = env.get("SERVER_ENV");
if (e.equals("dev")) {
propertiesFile = "environment/development.properties";
} else if (e.equals("prod")) {
propertiesFile = "environment/production.properties";
}
configurer.setLocation(new ClassPathResource(propertiesFile));
return configurer;
}
Then I have a test which looks like this
然后我有一个看起来像这样的测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:/spring/DealServiceTest-context.xml"})
@ActiveProfiles("test")
public class LogTest {
private static final Logger log = LogManager.getLogger(LogTest.class);
@Autowired
PathsService pathsService;
@Autowired
Environment environment;
@Test
public void testBeans(){
System.out.println("********** WASSUP from LogTest");
System.out.println(environment.getProperty("imageBucket"));
}
Although the test prints out null which indicates to me the properties file has not been loaded and prepared for its values to be injected. How can I achieve this?
尽管测试打印出 null 向我表明属性文件尚未加载并准备好注入其值。我怎样才能做到这一点?
回答by Zilvinas
You don't really need to set properties yourself, but you can do this using spring configuration. Check the documentation: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-profile-specific-properties
你真的不需要自己设置属性,但你可以使用 spring 配置来做到这一点。检查文档:https: //docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-profile-specific-properties
If you're using spring boot - all you need to do is create multiple properties file for your environments. And only for properties you need to override.
如果您使用的是 spring boot - 您需要做的就是为您的环境创建多个属性文件。并且仅适用于您需要覆盖的属性。
So your main properties file would be at
所以你的主要属性文件将在
src/main/resources/application.properties
Production
生产
src/main/resources/application-prod.properties
Development
发展
src/main/resources/application-dev.properties
Testing
测试
src/main/resources/application-test.properties
And then just use the profile name as your environment variable
然后只需使用配置文件名称作为您的环境变量
java -jar -Dspring.profiles.active=prod demo-0.0.1-SNAPSHOT.jar
回答by Anatoly Shamov
Actually, you can just use a placeholder in @PropertySource annotation. See documentation:
实际上,您可以只在 @PropertySource 注释中使用占位符。请参阅文档:
Any ${...} placeholders present in a @PropertySource resource location will be resolved against the set of property sources already registered against the environment.
Assuming that 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.
@PropertySource 资源位置中存在的任何 ${...} 占位符将根据已针对环境注册的一组属性源进行解析。
假设占位符存在于已经注册的属性源之一中,例如系统属性或环境变量,占位符将被解析为相应的值。
I've made a simple example, it receives a 'property.environment' value to choose, which .properties file should be used as property source. I have two resource files in my classpath - application-test.properties and application-dev.properties, each one contains a 'test.property' value ('test-env' and 'dev-env' respectively).
我做了一个简单的例子,它接收一个“ property.environment”值来选择,哪个 .properties 文件应该用作属性源。我的类路径中有两个资源文件——application-test.properties和 application-dev.properties,每个文件都包含一个“ test.property”值(分别是“test-env”和“dev-env”)。
Property configuration:
属性配置:
@Configuration
@PropertySource("classpath:/config/application-${property.environment}.properties")
public class PropertyConfig {
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
return propertySourcesPlaceholderConfigurer;
}
}
Component with @Value
带有@Value 的组件
@Component
public class TestService {
@Value("${test.property}")
String testProperty;
@PostConstruct
void init() {
System.out.println("---------------------------------------------------------");
System.out.println("Running in " + testProperty + " environment");
System.out.println("---------------------------------------------------------");
}
}
Build command line example (it runs tests with test environment properties)
构建命令行示例(它使用测试环境属性运行测试)
mvn clean install -DargLine="-Dproperty.environment=test"
Output
输出
---------------------------------------------------------
Running in test-env environment
---------------------------------------------------------
Run command line example
运行命令行示例
java -jar -Dproperty.environment=dev PATH_TO_YOUR_JAR.jar
Output
输出
---------------------------------------------------------
Running in dev-env environment
---------------------------------------------------------
回答by Per Huss
I would try to take advantage of the profile mechanism already in place in Spring. You basically have done the job yourself already, the only thing you need to change is to have different configurations for "test" and "production" profiles. I prefer to keep everything related to test away from production code (allowing me to place the TestConfig class below in the test source path), so I would probably do something like this:
我会尝试利用 Spring 中已经存在的配置文件机制。您基本上已经自己完成了这项工作,您唯一需要更改的是为“测试”和“生产”配置文件设置不同的配置。我更喜欢将与测试相关的所有内容远离生产代码(允许我将下面的 TestConfig 类放在测试源路径中),所以我可能会做这样的事情:
@Configuration
@Profile("!test")
@PropertySource(value = "classpath:/environment/production.properties")
@Import(AppConfig.class)
public class ProductionConfig
{
// Your production-specific config goes here
}
@Configuration
@Profile("test")
@PropertySource(value = "classpath:/environment/development.properties")
@Import(AppConfig.class)
public class TestConfig
{
// Your test-specific config goes here
}
@Configuration
public class AppConfig
{
// Needed for spring to handle ${property:default} syntax
@Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigIn() {
return new PropertySourcesPlaceholderConfigurer();
}
}
If you prefer to have one config for both cases, you can let the AppConfig
import the TestConfig
and the ProductionConfig
instead, but that will put test code in to production...
如果您更喜欢为这两种情况使用一个配置,您可以让AppConfig
导入TestConfig
和ProductionConfig
替代,但这会将测试代码投入生产......
Good luck with your project!
祝你的项目好运!
回答by laxman954
Don't hard code based on different environment, in spring boot you can able to maintain properties specific environment easily. Refer https://spapas.github.io/2016/03/31/spring-boot-settings/
不要根据不同的环境硬编码,在 spring boot 中你可以很容易地维护属性特定的环境。参考https://spapas.github.io/2016/03/31/spring-boot-settings/