name: General about: Bugs, enhancements, documentation, tasks. title: 'SpringBoot not aware of the Resource using along with @SpringBootApplication' labels: 'spring' assignees: ''


This is the basic springboot application that is an basic example of "https://github.com/anders-swanson/oracle-database-java-samples/blob/main/spring-resource-sample/pom.xml"

So using @Value inside the (along with) @SpringBootApplication, Resource is not awared of it. With seperate class, it works as expected. The code is below so anyone can reproduce it.

@Component
internal class DatabaseResourceResolver(
    private val jdbcClient: JdbcClient,
    @Value("\${databaseResource.table:spring_resource}") val table: String,
    @Value("\${databaseResource.blobColumn:file_data}") val blobColumn: String,
    @Value("\${databaseResource.fileNameColumn:file_name}") val fileNameColumn: String
): ResourceLoaderAware, ProtocolResolver {
    override fun resolve(location: String, resourceLoader: ResourceLoader): Resource? {
        if (location.startsWith(PROTOCOL_PREFIX)) {
            val fileName: String = location.substring(PROTOCOL_PREFIX.length)
            return DatabaseResource(jdbcClient = jdbcClient, table = table, blobColumn = blobColumn,
                fileName = fileName, fileNameColumn = fileNameColumn)
        }
        return null
    }
    override fun setResourceLoader(resourceLoader: ResourceLoader) {
        if (DefaultResourceLoader::class.java.isAssignableFrom(resourceLoader.javaClass)) {
            (resourceLoader as DefaultResourceLoader).addProtocolResolver(this)
        }
    }
}

class DatabaseResource(
    private val jdbcClient: JdbcClient,
    private val table: String,
    private val blobColumn: String,
    private val fileNameColumn: String,
    private val fileName: String
) : AbstractResource() {
    override fun getInputStream(): InputStream = content.inputStream()
    override fun contentLength(): Long = content.size.toLong()
    override fun exists(): Boolean =
        jdbcClient
            .sql(query)
            .query { rs: ResultSet, _: Int -> rs.next() }
            .single()
    override fun getDescription(): String = "{$PROTOCOL_PREFIX}$table/$fileName"
    override fun getFilename(): String = fileName

    private val content: ByteArray by lazy {
        jdbcClient
            .sql(query)
            .query { rs: ResultSet, _: Int -> rs.getBinaryStream(blobColumn).readAllBytes() }
            .single()
    }
    private val query: String = "select $blobColumn from $table where $fileNameColumn = '$fileName'"

    companion object {
        const val PROTOCOL_PREFIX: String = "postgresql://"
    }
}

So following will work;

@RestController
class SpringBootApplicationController(
    @Value("postgresql://cat.jpg") private val resource: Resource
) {
    @GetMapping("/cat")
    fun cat(): Resource = resource

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

But following will not aware of the Resource;

@SpringBootApplication
@RestController
class SpringKotlinResourceLoaderApplication(
    @Value("postgresql://cat.jpg") private val resource: Resource
){
    @GetMapping("/cat")
    fun cat(): Resource = resource

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

Db is basically 1 table(2 columns file_name and file_data) in postgresql

create table if not exists spring_resource (
    file_name  varchar(500) not null primary key,
    file_data  bytea not null
)

https://stackoverflow.com/questions/79395014/springboot-not-aware-of-the-resource-using-along-with-springbootapplication

Comment From: sdeleuze

I did a quick check with Kotlin + @Value populating a String property and it seems to work for both use cases.

Please share a reproducer as an attached archive or a link to a GitHub repository in order to make sure I do not miss a specific point of your use case (it is also easier and faster for us to test).

Comment From: senocak

Hi @sdeleuze thanks for the response but it is custom resource loading, so regular @value operations works as expected. It is not related with Kotlin, it is just i prefer to use. Same problem with Java as well. Here is the repository;

https://github.com/senocak/Spring-Kotlin-Resource-Loader

Just uncomment the SpringKotlinResourceLoaderApplication class and comment out the SpringBootApplicationController

Comment From: sdeleuze

Thanks for sharing the repro, I can indeed reproduce. What seems to happen here is that from a programming model perspective, there is no explicit signal that the class annotated with @SpringBootApplication, which is resolved early, has a dependency on the DatabaseResourceResolver bean.

I don't think there is something actionable on our side, so I would recommend to use @DependsOn("databaseResourceResolver") on the application class to make this dependency explicit.