如何在非Bean类(静态类)中读取application.yaml属性?
原实现的问题分析
- 类加载顺序风险:
FooUtil作为object会在首次被访问时初始化,若它在CustomEnvironmentPostProcessor.postProcessEnvironment执行前就被加载,拿到的会是property的默认值true,而非配置文件中的实际值——因为Spring的EnvironmentPostProcessor执行时机在应用启动早期,但类加载顺序不受Spring控制。 - 违背Spring设计规范:
EnvironmentPostProcessor的定位是修改环境配置的处理器,本身应保持无状态。用单例object持有可变配置属性,既违背了该接口的设计初衷,在多环境初始化场景下还可能引发线程安全问题。 - 默认值维护性差:默认值硬编码在代码中,没有利用Spring配置的默认值机制,后续修改不够灵活。
推荐改进方案
方案1:使用@ConfigurationProperties绑定配置(Spring官方推荐)
这种方式能优雅完成配置绑定,彻底规避类加载顺序问题。
首先定义配置类:
@ConfigurationProperties(prefix = "foo") class FooProperties { var bar: Boolean = true // 在这里设置默认值 }
在启动类添加注解启用配置绑定:
@SpringBootApplication @EnableConfigurationProperties(FooProperties::class) class YourApplication fun main(args: Array<String>) { runApplication<YourApplication>(*args) }
若FooUtil必须是静态类,可通过Spring上下文注入配置类,用@PostConstruct完成静态属性初始化:
@Component object FooUtil { private lateinit var fooProperties: FooProperties @Autowired fun setFooProperties(properties: FooProperties) { fooProperties = properties } // 示例业务方法 fun doSomething() { val barValue = fooProperties.bar // 编写业务逻辑 } }
注意:给
FooUtil加上@Component注解,确保Spring能扫描到并自动注入FooProperties。
方案2:直接注入Environment到工具类(适合简单场景)
如果不想使用@ConfigurationProperties,可以直接将Spring的Environment注入工具类,延迟获取配置值:
@Component object FooUtil { @Autowired private lateinit var environment: Environment private val barProperty: Boolean by lazy { environment.getProperty("foo.bar", Boolean::class.java) ?: true } fun useProperty() { println(barProperty) } }
这种方式避免了提前初始化问题,因为每次调用相关方法时才从Environment中取值,而Environment在Spring启动完成后已包含所有配置。
方案3:保留EnvironmentPostProcessor但修正实现(不推荐,仅作参考)
若一定要用EnvironmentPostProcessor,需让它回归本职工作(处理环境配置),配置读取交给工具类通过Spring上下文完成:
class CustomEnvironmentPostProcessor : EnvironmentPostProcessor { private const val CONFIG_PROPERTY = "foo.bar" override fun postProcessEnvironment(environment: ConfigurableEnvironment, application: SpringApplication?) { val defaultValue = "true" val propertyValue = StringUtils.trimWhitespace(environment.getProperty(CONFIG_PROPERTY) ?: defaultValue) // 若原配置不存在,将默认值加入环境 if (!environment.containsProperty(CONFIG_PROPERTY)) { environment.propertySources.addFirst( MapPropertySource("custom-foo-bar", mapOf(CONFIG_PROPERTY to propertyValue)) ) } } } @Component object FooUtil { @Autowired private lateinit var environment: Environment private val barProperty: Boolean by lazy { environment.getProperty("foo.bar", Boolean::class.java) ?: true } fun useProperty() { println(barProperty) } }
内容的提问来源于stack exchange,提问作者developerr
相关产品推荐
相关产品推荐

