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