@ConstructorBinding
@ConfigurationProperties("auth")
data class AuthProperties(
val client: Client,
...
)
data class Client(
// several secretive properties
) {
private lateinit var uuids: ConcurrentLinkedQueue<Pair<String, Instant>>
val uuid: String
get() {
// return the top item from the queue
}
set(value) {
// parse string and insert into queue
}
}
Problems:
- Property setter doesn't get called. The traditional way would be to write a
Converter
, but that also has issues as listed below; Kotlin way is simply calling the setter. It could be that the@ConstructorBinding
is interfering with the setter, but that's on the top level class, not on nested ones. I suggest that the seemingly useless@NestedConfigurationProperty
class could be allowed on theClient
, and if a corresponding@ConstructorBinding
is not specified, setters are called instead. Doing so would allow each nested class to override the parents instantiation strategy. - Changing the definition to
inner class Client
doesn't work. What if the inner class wants access to properties of the outer? To make things worse, it shows a completely misleading error message.
Failed to bind properties under 'auth.client' to mycompany.AuthProperties$Client:
Property: auth.client.port
Value: 80
Origin: class path resource [application.yml]:5:11
Reason: Failed to bind properties under 'auth.client' to mycompany.AuthProperties$Client
- To workaround the problem in 1, I create a first-class binding candidate
val uuids: Queue<Pair<String, Instant>>
inClient
, then reference it inuuid.get
. Except now I get an error sayingQueue
is not an allowed type. Why not, since I'm providing aConverter
for it?
@ConfigurationPropertiesBinding
Property: auth.client.uuids
Value: 6196c660a289
Origin: class path resource [application.yml]:11:14
Reason: Unsupported Collection interface: java.util.Queue
- So I create a more specific type,
val uuids: ConcurrentLinkedQueue<Pair<String, Instant>>
and create aConverter<String, Queue<Pair<String, Instant>>
; however, it fails.
Reason: No converter found capable of converting from type [java.lang.String] to type [java.util.List<kotlin.Pair<java.lang.String, java.time.Instant>>]
It appears the binder is picking the first of all superinterfaces and trying to find a matching converter. A ConcurrentLinkedQueue
has several superinterfaces, all should be tried one by one.
5. I can't find a way to write a Converter
that works in Kotlin, so I write one in Java as Converter<String, List<Pair<String, Instant>>>
. This is problematic because I don't want a List
, and I should not have to switch to Java.
6. Having an @Autowired
in the Client
class doesn't get set.
7. If the application.yml
doesn't exist (this class was in a library module that didn't have one during testing), the binding doesn't tell me that, instead fails with a failed-to-invoke-constructor error (I don't have the stacktrace for it).
Comment From: wilkinsona
There's quite a lot going on here and the code snippet as provided doesn't compile. Filling in the blanks, changing the uuid
property to var
, etc, is unlikely to result in the same code as you have so we may end up diagnosing a different problem.
It looks like you're trying to mix constructor binding and JavaBean binding. This isn't supported with Java or Kotlin. If I've misinterpreted the intent of the code snippet and you would like us to spend some more time investigating, please spend some time providing a complete yet minimal sample that reproduces the problem. You can share it with us by pushing it to a separate repository on GitHub or by zipping it up and attaching it to this issue.
Comment From: spring-projects-issues
If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.
Comment From: spring-projects-issues
Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.