【发布时间】:2020-03-02 15:52:31
【问题描述】:
问题陈述
我想在初始化 bean 之前从类路径中或外部位置的属性文件加载属性。这些属性也是 Bean 初始化的一部分。我无法从 Spring 的标准 application.properties 或其自定义中自动装配属性,因为同一个属性文件必须可由多个可部署对象访问。
我尝试了什么
我知道Spring Application Events;其实我已经上钩了 ContextRefreshedEvent 在 Spring Context 初始化后执行一些任务(在这个阶段也初始化了 Beans)。
对于我的问题陈述,来自 Spring Docs ApplicationEnvironmentPreparedEvent 的描述看起来很有希望,但钩子不起作用。
@SpringBootApplication
public class App {
public static void main(String[] args) throws IOException {
SpringApplication.run(App.class, args);
}
@EventListener
public void onStartUp(ContextRefreshedEvent event) {
System.out.println("ContextRefreshedEvent"); // WORKS
}
@EventListener
public void onShutDown(ContextClosedEvent event) {
System.out.println("ContextClosedEvent"); // WORKS
}
@EventListener
public void onEvent6(ApplicationStartedEvent event) {
System.out.println("ApplicationStartedEvent"); // WORKS BUT AFTER ContextRefreshedEvent
}
@EventListener
public void onEvent3(ApplicationReadyEvent event) {
System.out.println("ApplicationReadyEvent"); // WORKS WORKS BUT AFTER ContextRefreshedEvent
}
public void onEvent1(ApplicationEnvironmentPreparedEvent event) {
System.out.println("ApplicationEnvironmentPreparedEvent"); // DOESN'T WORK
}
@EventListener
public void onEvent2(ApplicationContextInitializedEvent event) {
System.out.println("ApplicationContextInitializedEvent"); // DOESN'T WORK
}
@EventListener
public void onEvent4(ApplicationContextInitializedEvent event) {
System.out.println("ApplicationContextInitializedEvent");
}
@EventListener
public void onEvent5(ContextStartedEvent event) {
System.out.println("ContextStartedEvent");
}
}
更新
按照 cmets 中 M.Deinum 的建议,我尝试添加如下所示的应用程序上下文初始化程序。它似乎也不起作用。
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(App.class)
.initializers(applicationContext -> {
System.out.println("INSIDE CUSTOM APPLICATION INITIALIZER");
})
.run(args);
}
更新 #2
虽然我的问题陈述是关于加载属性,但我的问题/好奇实际上是关于如何在将类初始化为 bean 并放入 Spring IoC 容器之前运行一些代码。现在,这些 bean 在初始化期间需要一些属性值,我不能/不想自动装配它们,原因如下:
如 cmets 和 answers 中所述,同样可以使用 Spring Boot 的外部化配置和配置文件来完成。但是,我需要分别维护应用程序属性和与域相关的属性。一个基本域属性应该至少有 100 个属性,并且数量会随着时间的推移而增长。应用程序属性和与域相关的属性都有一个用于不同环境(开发、SIT、UAT、生产)的属性文件。属性文件覆盖一个或多个基本属性。那是 8 个属性文件。现在,需要将同一个应用程序部署到多个地区。这使它成为8 * n 属性文件,其中n 是地理区域的数量。我希望所有属性文件都存储在一个公共模块中,以便不同的部署可以访问它们。环境和地理在运行时将被称为系统属性。
虽然这些可以通过使用 Spring 配置文件和优先顺序来实现,但我希望对其进行编程控制(我还将维护自己的属性存储库)。例如。我会编写一个名为MyPropUtil 的便利实用程序并像这样访问它们:
public class MyPropUtil {
private static Map<String, Properties> repository;
public static initialize(..) {
....
}
public static String getDomainProperty(String key) {
return repository.get("domain").getProperty(key);
}
public static String getAppProperty(String key) {
return repository.get("app").getProperty(key);
}
public static String getAndAddBasePathToAppPropertyValue(String key) {
...
}
}
@Configuration
public class MyComponent {
@Bean
public SomeClass getSomeClassBean() {
SomeClass obj = new SomeClass();
obj.someProp1(MyPropUtil.getDomainProperty('domainkey1'));
obj.someProp2(MyPropUtil.getAppProperty('appkey1'));
// For some properties
obj.someProp2(MyPropUtil.getAndAddBasePathToAppPropertyValue('some.relative.path.value'));
....
return obj;
}
}
从文档来看,ApplicationEvents 和 ApplicationInitializers 似乎符合我的需要,但我无法让它们为我的问题陈述工作。
【问题讨论】:
-
为什么不添加
spring.config.additional-locations工作?或者只是通过spring.config.location提供所有配置文件?我不明白你为什么需要自己加载它们?这些位置可以是您想要加载的外部位置(只需使用file:前缀从文件系统加载它们)。 -
你试过@PreConstruct吗?
-
@M.Deinum 这有点复杂。只是谈论可能的属性文件的数量 - 产品应该是多个地理区域,每个地理区域都有自己的开发、UAT 和生产版本的属性,我需要能够设置优先级;除此之外,我还有一个包装器(PropertyUtility)来获取和处理基于不同上下文的属性值。 (我可以再次使用
@Configuration类执行此操作,但属性太多。) -
@medTech 这行不通,因为我需要将多个属性文件加载到属性存储库,而不仅仅是几个 bean。
-
这就是配置文件的用途(每个环境的不同配置),优先级是您指定它们的顺序。无论使用框架而不是围绕它工作。您想使用 Spring 工具但不使用框架。如果您真的想做一些您认为不可能的复杂事情,请使用 ApplicationContextInitializer 实现来执行此操作并注册其他 PropertySources(不要与 @PropertySource 混淆!)。
标签: java spring spring-boot