You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SpringBoot中@SpringBootApplication类内@Value无法识别自定义Resource

问题原因

当@SpringBootApplication与@RestController标注在同一个类时,该类是Spring Boot启动过程中最早被实例化的Bean之一。此时ApplicationContext还处于初始化早期阶段:

  • 自定义的DatabaseResourceResolver作为@Component,还未被实例化,更未通过ResourceLoaderAware的setResourceLoader方法注册到DefaultResourceLoader中;
  • 构造函数注入时,Spring会立即解析@Value("postgresql://cat.jpg")的资源路径,但此时ResourceLoader没有对应的ProtocolResolver,只能使用默认逻辑加载,自然无法识别自定义协议,导致加载失败。

而独立的@RestController类会在ApplicationContext刷新的后续Bean创建阶段被实例化,此时DatabaseResourceResolver已经完成注册,因此能正常解析自定义协议的资源。

解决方案

方案1:使用@Lazy延迟字段注入

将构造函数注入改为字段注入,并添加@Lazy注解,让资源解析延迟到第一次使用时(此时DatabaseResourceResolver已完成注册):

@SpringBootApplication
@RestController
class SpringKotlinResourceLoaderApplication {
    @Lazy
    @Value("postgresql://cat.jpg")
    private lateinit var resource: Resource

    @GetMapping("/cat")
    fun cat(): Resource = resource

    @GetMapping("/isCat")
    fun isCat(): Boolean = resource.exists()
}

方案2:提前注册ProtocolResolver

通过ApplicationContextInitializer在ApplicationContext初始化早期就注册自定义ProtocolResolver,确保主类构造注入时Resolver已可用:

  1. 实现ApplicationContextInitializer:
class DatabaseResourceResolverInitializer : ApplicationContextInitializer<GenericApplicationContext> {
    override fun initialize(context: GenericApplicationContext) {
        val jdbcClient = context.getBean(JdbcClient::class.java)
        val table = context.environment.getProperty("databaseResource.table", "spring_resource")
        val blobColumn = context.environment.getProperty("databaseResource.blobColumn", "file_data")
        val fileNameColumn = context.environment.getProperty("databaseResource.fileNameColumn", "file_name")
        
        val resolver = DatabaseResourceResolver(jdbcClient, table, blobColumn, fileNameColumn)
        context.addProtocolResolver(resolver)
    }
}
  1. 注册Initializer:
    在项目的src/main/resources/META-INF/spring.factories文件中添加:
org.springframework.context.ApplicationContextInitializer=com.yourpackage.DatabaseResourceResolverInitializer

(替换com.yourpackage为实际包名)

方案3:使用Provider延迟获取资源

通过javax.inject.Provider(或Spring的ObjectProvider)包装资源,延迟到第一次调用get()时再解析资源:

import javax.inject.Provider

@SpringBootApplication
@RestController
class SpringKotlinResourceLoaderApplication(
    private val resourceProvider: Provider<@Value("postgresql://cat.jpg") Resource>
){
    @GetMapping("/cat")
    fun cat(): Resource = resourceProvider.get()

    @GetMapping("/isCat")
    fun isCat(): Boolean = resourceProvider.get().exists()
}

内容的提问来源于stack exchange,提问作者Anıl Şenocak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 15:50:08