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已可用:
- 实现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) } }
- 注册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
相关产品推荐
相关产品推荐

