spring-cloud-consul: Spring Config Client + Consul Discovery First Bootstrap - Encountering `Binder has not been registered` error when accessing `/actuator/refresh`

Hi All,

I am encountering the below error upon calling /actuator/refresh on Config Client using Discovery First Bootstrap approach with Consul.

Using Spring Boot v2.4.1 with Spring Cloud Config Dependencies v3.0.1

2021-02-07 11:42:11.759  INFO 22516 --- [nio-8082-exec-1] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at : http://192.168.0.147:8800/
2021-02-07 11:42:12.654  INFO 22516 --- [nio-8082-exec-1] c.c.c.ConfigServicePropertySourceLocator : Located environment: name=common, service-b, profiles=[dev], label=null, version=f505ce9985c13881051c59bec644ef490a6c5d15, state=null
2021-02-07 11:42:12.655  INFO 22516 --- [nio-8082-exec-1] b.c.PropertySourceBootstrapConfiguration : Located property source: [BootstrapPropertySource {name='bootstrapProperties-configClient'}, BootstrapPropertySource {name='bootstrapProperties-https://github.com/2much2learn/article_2021_jan_spring-cloud-config-configfirst_discoveryfirst/Config resource 'file [C:\Users\narra\AppData\Local\Temp\config-repo-5281483341513251368\git-config-store\service-b.yaml' via location 'git-config-store/' (document spring-cloud/spring-cloud-config#1)'}, BootstrapPropertySource {name='bootstrapProperties-https://github.com/2much2learn/article_2021_jan_spring-cloud-config-configfirst_discoveryfirst/Config resource 'file [C:\Users\narra\AppData\Local\Temp\config-repo-5281483341513251368\git-config-store\service-b.yaml' via location 'git-config-store/' (document #0)'}, BootstrapPropertySource {name='bootstrapProperties-https://github.com/2much2learn/article_2021_jan_spring-cloud-config-configfirst_discoveryfirst/file:C:\Users\narra\AppData\Local\Temp\config-repo-5281483341513251368\git-config-store\common.yaml'}]
2021-02-07 11:42:12.699 ERROR 22516 --- [nio-8082-exec-1] o.s.boot.SpringApplication               : Application run failed

java.lang.IllegalStateException: org.springframework.boot.context.properties.bind.Binder has not been registered
        at org.springframework.boot.DefaultBootstrapContext.lambda$get$1(DefaultBootstrapContext.java:88) ~[spring-boot-2.4.1.jar!/:2.4.1]
        at org.springframework.boot.DefaultBootstrapContext.getOrElseThrow(DefaultBootstrapContext.java:109) ~[spring-boot-2.4.1.jar!/:2.4.1]
        at org.springframework.boot.DefaultBootstrapContext.get(DefaultBootstrapContext.java:88) ~[spring-boot-2.4.1.jar!/:2.4.1]
        at org.springframework.cloud.consul.discovery.configclient.ConsulConfigServerBootstrapper.lambda$intitialize$3(ConsulConfigServerBootstrapper.java:54) ~[spring-cloud-consul-discovery-3.0.0.jar!/:3.0.0]
        at org.springframework.boot.DefaultBootstrapContext.getInstance(DefaultBootstrapContext.java:119) ~[spring-boot-2.4.1.jar!/:2.4.1]
        at org.springframework.boot.DefaultBootstrapContext.getOrElseThrow(DefaultBootstrapContext.java:111) ~[spring-boot-2.4.1.jar!/:2.4.1]
        at org.springframework.boot.DefaultBootstrapContext.get(DefaultBootstrapContext.java:88) ~[spring-boot-2.4.1.jar!/:2.4.1]
        at org.springframework.cloud.consul.discovery.configclient.ConsulConfigServerBootstrapper.lambda$intitialize$4(ConsulConfigServerBootstrapper.java:67) ~[spring-cloud-consul-discovery-3.0.0.jar!/:3.0.0

I tried the same with Config First Bootstrap approach and can see /actuator/refresh working as expected by reloading the configs from config-server

Sample Code is available in git repo - https://github.com/2much2learn/article_2021_jan_spring-cloud-config-configfirst_discoveryfirst

Run the below series of commands to replicate the issue

-- Run Consul Container
$ docker run -d --name consul-server -p 8500:8500 -e CONSUL_BIND_INTERFACE=eth0 consul

-- Clone and build the packages
$ git clone https://github.com/2much2learn/article_2021_jan_spring-cloud-config-configfirst_discoveryfirst.git
$ cd article_2021_jan_spring-cloud-config-configfirst_discoveryfirst
$ cd spring-multi-module-cloud-config-consul-discovery
$ mvn clean package

-- Start config-server
$ java -jar -Dspring.profiles.active=git config-server\target\config-server-0.0.1-SNAPSHOT.jar

-- Start spring boot service
$ java -jar service-b\target\service-b-0.0.1-SNAPSHOT.jar

-- Access service endpoint
$ curl http://localhost:8082/greeting

-- refresh service to reload configs
$ curl -H "Content-Type: application/json" -d {} http://localhost:8082/actuator/refresh

Any help would be appriciated.

Thanks, Madan N

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 16 (3 by maintainers)

Most upvoted comments

@spencergibb thanks for fixing this.

Thanks @pcoates33. This works for us.

The problem occurs when you use actuator/refresh and have both of the following dependencies

  • org.springframework.cloud:spring-cloud-starter-bootstrap
  • org.springframework.cloud:spring-cloud-starter-consul-discovery

The bootstrap starter means that refresh will be performed by org.springframework.cloud.context.refresh.LegacyContextRefresher this runs a SpringApplication with a reduced set of listeners

// Just the listeners that affect the environment (e.g. excluding logging
// listener because it has side effects)
builder.application().setListeners(
	Arrays.asList(new BootstrapApplicationListener(), new BootstrapConfigFileApplicationListener()));
capture = builder.run();

The consul discovery adds org.springframework.cloud.consul.discovery.configclient.ConsulConfigServerBootstrapper to the SpringApplication run bootstrap process. This adds the closeListener for the bootstrapContext close event which throws the exception when no Binder is present.

On application start, the Binder is added by org.springframework.boot.context.config.ConfigDataEnvironment, which is called from ConfigDataEnvironmentPostProcessor, which is called from EnvironmentPostProcessorApplicationListener.

However, on a refresh, the LegacyContextRefresher does not add the EnvironmentPostProcessorApplicationListener, which means no Binder is registered. So, the “Binder has not been registered” exception occurs when the close listener in ConsulConfigServerBootstrapper is called.

Adding the EnvironmentPostProcessorApplicationListener to LegacyContextRefresher makes the refresh work OK.

// Just the listeners that affect the environment (e.g. excluding logging
// listener because it has side effects)
builder.application().setListeners(
	Arrays.asList(new BootstrapApplicationListener(), new BootstrapConfigFileApplicationListener(),
		new EnvironmentPostProcessorApplicationListener()));
capture = builder.run();

@royremi I did, but I’ve already removed it.