Talk about ConfigClientWatch of Spring Cloud Config.



Spring Cloud Config provides a ConfigClientWatch function, which can periodically poll the status of client configuration and refresh if the status changes.

Configuration file

      uri: http://localhost:8888
        enabled: true
        initialDelay: 5000 ##default 180000 ms
        delay: 10000 ##default 500 ms

Configuration class

spring-cloud-config-client-1.3.1.RELEASE-sources.jar! /org/springframework/cloud/config/client/

    @ConditionalOnProperty(value = "")
    protected static class ConfigClientWatchConfiguration {

        public ConfigClientWatch configClientWatch(ContextRefresher contextRefresher) {
            return new ConfigClientWatch(contextRefresher);


public class ConfigClientWatch implements Closeable, EnvironmentAware {

    private static Log log = LogFactory

    private final AtomicBoolean running = new AtomicBoolean(false);
    private final ContextRefresher refresher;
    private Environment environment;

    public ConfigClientWatch(ContextRefresher refresher) {
        this.refresher = refresher;

    public void setEnvironment(Environment environment) {
        this.environment = environment;

    public void start() {
        this.running.compareAndSet(false, true);

    @Scheduled(initialDelayString = "${}", fixedDelayString = "${}")
    public void watchConfigServer() {
        if (this.running.get()) {
            String newState = this.environment.getProperty("config.client.state");
            String oldState = ConfigClientStateHolder.getState();

            // only refresh if state has changed
            if (stateChanged(oldState, newState)) {

    /* for testing */ boolean stateChanged(String oldState, String newState) {
        return (!hasText(oldState) && hasText(newState))
                || (hasText(oldState) && !oldState.equals(newState));

    public void close() {
        this.running.compareAndSet(true, false);


Rely on the environment variable of config.client.state to judge the state of the client-side configuration file.
Rely on ContextRefresher to refresh the configuration/instance


spring-cloud-context-1.2.2.RELEASE-sources.jar! /org/springframework/cloud/context/refresh/

public synchronized Set<String> refresh() {
        Map<String, Object> before = extract(
        Set<String> keys = changes(before,
        this.context.publishEvent(new EnvironmentChangeEvent(keys));
        return keys;

This refresh mainly does two things:

  • The first is the release of the EnvironmentChangeEvent event

  • The second thing is to call the refreshAll method of RefreshScope.

Listener for EnvironmentChangeEvent

spring-cloud-context-1.2.2.RELEASE-sources.jar! /org/springframework/cloud/context/properties/

public class ConfigurationPropertiesRebinder
        implements ApplicationContextAware, ApplicationListener<EnvironmentChangeEvent> {

    private ConfigurationPropertiesBeans beans;

    private ConfigurationPropertiesBindingPostProcessor binder;

    private ApplicationContext applicationContext;

    private Map<String, Exception> errors = new ConcurrentHashMap<>();

    public ConfigurationPropertiesRebinder(
            ConfigurationPropertiesBindingPostProcessor binder,
            ConfigurationPropertiesBeans beans) {
        this.binder = binder;
        this.beans = beans;

    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        this.applicationContext = applicationContext;

     * A map of bean name to errors when instantiating the bean.
     * @return the errors accumulated since the latest destroy
    public Map<String, Exception> getErrors() {
        return this.errors;

    public void rebind() {
        for (String name : this.beans.getBeanNames()) {

    public boolean rebind(String name) {
        if (!this.beans.getBeanNames().contains(name)) {
            return false;
        if (this.applicationContext != null) {
            try {
                Object bean = this.applicationContext.getBean(name);
                if (AopUtils.isCglibProxy(bean)) {
                    bean = getTargetObject(bean);
                this.binder.postProcessBeforeInitialization(bean, name);
                        .initializeBean(bean, name);
                return true;
            catch (RuntimeException e) {
                this.errors.put(name, e);
                throw e;
        return false;

    private static <T> T getTargetObject(Object candidate) {
        try {
            if (AopUtils.isAopProxy(candidate) && (candidate instanceof Advised)) {
                return (T) ((Advised) candidate).getTargetSource().getTarget();
        catch (Exception ex) {
            throw new IllegalStateException("Failed to unwrap proxied object", ex);
        return (T) candidate;

    public Set<String> getBeanNames() {
        return new HashSet<String>(this.beans.getBeanNames());

    public void onApplicationEvent(EnvironmentChangeEvent event) {


RefreshAll of RefreshScope

spring-cloud-context-1.2.2.RELEASE-sources.jar! /org/springframework/cloud/context/scope/refresh/

@ManagedOperation(description = "Dispose of the current instance of all beans in this scope and force a refresh on next method execution.")
    public void refreshAll() {
        this.context.publishEvent(new RefreshScopeRefreshedEvent());

Publishing RefreshScopeRefreshedEvent Event

RefreshScopeRefreshedEvent event listener

spring-cloud-netflix-eureka-client-1.3.1.RELEASE-sources.jar! /org/springframework/cloud/netflix/eureka/

    protected static class EurekaClientConfigurationRefresher {

        @Autowired(required = false)
        private EurekaClient eurekaClient;

        @Autowired(required = false)
        private EurekaAutoServiceRegistration autoRegistration;

        public void onApplicationEvent(RefreshScopeRefreshedEvent event) {
            //This will force the creation of the EurkaClient bean if not already created
            //to make sure the client will be reregistered after a refresh event
            if(eurekaClient != null) {
            if (autoRegistration != null) {
                // register in case meta data changed

spring-cloud-netflix-core-1.2.6.RELEASE-sources.jar! /org/springframework/cloud/netflix/zuul/

    public ApplicationListener<ApplicationEvent> zuulRefreshRoutesListener() {
        return new ZuulRefreshListener();
private static class ZuulRefreshListener
            implements ApplicationListener<ApplicationEvent> {

        private ZuulHandlerMapping zuulHandlerMapping;

        private HeartbeatMonitor heartbeatMonitor = new HeartbeatMonitor();

        public void onApplicationEvent(ApplicationEvent event) {
            if (event instanceof ContextRefreshedEvent
                    || event instanceof RefreshScopeRefreshedEvent
                    || event instanceof RoutesRefreshedEvent) {
            else if (event instanceof HeartbeatEvent) {
                if (this.heartbeatMonitor.update(((HeartbeatEvent) event).getValue())) {



Spring Cloud Config’s code provides ConfigClientWatch, but when git is used as config server, the state obtained when pulling configuration is always null, so client polling cannot achieve refresh effect. The second one is the refreshments released by the refreshments. eureka will UPdate the registration information to DOWN first and then up again. this frequent operation is somewhat risky.