spring-cloud / spring-cloud/spring-cloud-commons

Kotlin: variables in constructor with @ConfigurationProperties can not refresh

Open
#1,029 3 comments 7 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

waiting-for-triage
Dominant language
Java
Stars
751
Forks
744
Avg merge
1d 14h
Merged PRs (30d)
9

Description

Moved from spring-cloud-kubernetes.

I found a similar issue #823. Finally the author said:

You were right. The issue was with Kotlin Data class which is immutable in nature. Changing this to something like this @ConfigurationProperties(prefix = "myapp") @ConstructorBinding class MyApConfig { lateinit var name: String lateinit var message: String } works.

I don't agree with this view. Data class is not completely immutable, you can make it mutable by var. After further testing, I found that it had nothing to do with data class. The key lies in the position of the variable definition. If the variable is defined in the constructor, it is not refreshed, although it is mutable (var).

// won't refresh
@ConstructorBinding
@ConfigurationProperties(prefix = "bean")
class TestProps(
    var message: String
)

// will refresh
@ConstructorBinding
@ConfigurationProperties(prefix = "bean")
class TestProps {
    lateinit var message: String
}

The only difference between the two style is whether delay initialization is allowed. Maybe that's the cause. But I don't know the exact principle of refresh and there's no log. Hope someone can investigate further.

Reproduction step
  • Spring Boot: 2.4.8
  • Spring Cloud: 2020.0.4

Dependencies:

implementation("org.springframework.cloud:spring-cloud-commons")
implementation("org.springframework.cloud:spring-cloud-starter-bootstrap")
implementation("org.springframework.cloud:spring-cloud-starter-config")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-actuator")

bootstrap.yml:

spring:
  config:
    import: file:/path/to/application.yml
  1. Start the app.
  2. Change /path/to/application.yml.
  3. POST /actuator/refresh, got ["bean.message"] (should refresh successfully)
  4. Confirm that there is no actual refresh.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the issue using bootstrap.yml, the external application.yml, and POST /actuator/refresh with constructor-defined and class-body Kotlin properties. Trace the configuration refresh path and add a regression test for the constructor case; done means the changed property is actually refreshed.

Written by the indexing model from the issue text.

Assessment

Tech stack
kotlin, spring, spring-boot
Domain
backend, cloud
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.