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.