Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Refresh drops @ConfigurationProperties values that no property source carries

Since spring-cloud-context 5.0.2, ConfigurationPropertiesRebinder resets every rebindable bean to the defaults of a template instance before re-binding it. Whatever was set programmatically after the initial binding is gone, because the re-binding only restores what the Environment carries.

EurekaInstanceConfigBean loses ipAddress, metadataMap and nonSecurePort. The instance is then cancelled from the registry and cannot register again: the server answers 400.

Java 25, Spring Boot 4.1.1, Spring Cloud 2025.1.3, spring-cloud-context 5.0.3, spring-cloud-netflix-eureka-client 5.0.2.

Run

demo-eureka (8761), demo-configserver (8888), then demo-refresh (8080), which contains no code beyond the generated @SpringBootApplication.

cd demo-eureka       && ./mvnw spring-boot:run
cd demo-configserver && ./mvnw spring-boot:run
cd demo-refresh      && ./mvnw spring-boot:run \
    -Dspring-boot.run.jvmArguments="-Dspring.application.instance_id=xxx" \
    -Dspring-boot.run.arguments="--management.endpoint.configprops.show-values=ALWAYS"

props() { curl -s localhost:8080/actuator/configprops \
  | jq -c '..|objects|select(.prefix=="eureka.instance")|.properties
           |{hostname,ipAddress,metadataMap,nonSecurePort}'; }

props; curl -s -X POST localhost:8080/actuator/refresh; props

Result

refresh -> []                               no key of the Environment changed

BEFORE                                      AFTER
hostname      : localhost                   hostname      : localhost
ipAddress     : 192.168.1.117               ipAddress     : null
metadataMap   : {management.port=8080}      metadataMap   : {}
nonSecurePort : 8080                        nonSecurePort : 80

An empty change set is not a condition, only the shortest proof that the refresh itself drives the reset. hostname survives because application.yaml carries it.

Registered instance DEMO-REFRESH/demo-refresh:8080:xxx with status UP
Cancelled instance DEMO-REFRESH/demo-refresh:8080:xxx
registering service... -> status code 400 -> registration failed
was unable to send heartbeat!

ApplicationResource.addInstance answers 400 Missing ip address for a blank ipAddr. The Eureka server runs with defaults; the run logs one explicit cancel, zero evictions.

Why

resetBeanToDefaults builds its template through any no-arg constructor, whatever its visibility:

for (Constructor<?> constructor : type.getDeclaredConstructors()) {
    if (constructor.getParameterCount() == 0) { return true; }
}

EurekaInstanceConfigBean has a private EurekaInstanceConfigBean() {} next to the public EurekaInstanceConfigBean(InetUtils) that computes ipAddress and hostname. The template is built with the private one, so it carries ipAddress = null, nonSecurePort = 80 and an empty metadataMap, and those get written onto the live bean. The re-binding restores nothing, because:

  • ipAddress comes from the public constructor,
  • nonSecurePort from EurekaAutoServiceRegistration (Updating port to 8080),
  • metadataMap[management.port] from EurekaClientAutoConfiguration:204.

rebind() iterates every bean whatever the changed keys, so /actuator/busrefresh behaves the same. The registration breaks because eureka.client.refresh.enable defaults to true: EurekaClient and ApplicationInfoManager are refresh scoped, refreshAll() destroys them, the shutdown cancels the lease, and the new InstanceInfo is built from the reset bean.

Versions

Absent in 5.0.1, introduced in 5.0.2 by #1680, extended in 5.0.3.

cd demo-refresh && ./mvnw spring-boot:run -Pcontext-5.0.1 \
    -Dspring-boot.run.jvmArguments="-Dspring.application.instance_id=xxx"

With 5.0.1 the same refresh leaves the bean untouched. #1680 targets a property removed from the Environment while the bean keeps its value; the reset cannot tell that apart from a value that was never in the Environment.

What hides it

  • spring.cloud.refresh.never-refreshable=com.zaxxer.hikari.HikariDataSource,org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean skips the bean. The property replaces the default, so Hikari has to be repeated.
  • eureka.client.refresh.enable=false does not prevent the reset, it only stops anything from rebuilding an InstanceInfo from the reset bean.
  • Declaring the values under eureka.instance.* puts them where the re-binding can restore them.

Related

#1727, same pull request: nested objects pass through null during the rebind and concurrent threads get an NPE. Transient, traffic dependent. This one is the state the bean is left in, on an idle application.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages