Utilisation de @ConfigurationProperties avec Apollo pour la mise à jour dynamique des configurations

Dans un projet récent, j'ai utilisé l'annotation @ConfigurationProperties pour initialiser une configuration sous forme de Map. La structure de la configuration est la suivante (le code et la configuraton réels peuvent varier, ceci est uniquement à titre d'exemple) :

file.pathMap[127.0.0.1] = [{"path": "/videofile1"}]
file.pathMap[127.0.0.2] = [{"path": "/videofile2"}]
file.pathMap[127.0.0.3] = [{"path": "/videofile3"}]

Le but principal est de configurer différents chemins en fonction de l'IP.

Voici comment j'ai défini la classe de configuration dans le code :

@Configuration
@ConfigurationProperties(prefix = "file")
public class CheminFichierProprietes {
    private Map<String, String> cheminMap;

    public Map<String, String> getCheminMap() {
        return cheminMap;
    }

    public void setCheminMap(Map<String, String> cheminMap) {
        this.cheminMap = cheminMap;
    }
}

J'utilise Apollo pour gérer les configurations, mais j'ai constaté que les modifications apportées via Apollo ne se reflètent pas automatiquement dans l'application. Après quelques recherches, j'ai découvert qu'il est nécessaire d'utiliser EnvironmentChangeEvent ou RefreshScope pour que les valeurs injectées par @ConfigurationProperties soient mises à jour dynamiquement. Voici un exemple de code proposé pour résoudre ce problème :

import com.ctrip.framework.apollo.model.ConfigChange;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class ChangementConfigApollo implements ApplicationContextAware {
    private ApplicationContext applicationContext;

    @ApolloConfigChangeListener
    private void someChangeHandler(ConfigChangeEvent changeEvent) {
        for (String key : changeEvent.changedKeys()) {
            ConfigChange change = changeEvent.getChange(key);
            log.info("Found change - {}", change.toString());
        }

        this.applicationContext.publishEvent(new EnvironmentChangeEvent(changeEvent.changedKeys()));
    }

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

Cependant, mon projet n'utilies pas Spring Cloud, donc je ne peux pas utilsier EnvironmentChangeEvent. J'ai donc décidé d'écrire ma propre logique pour mettre à jour la configuration lorsqu'un événement de changement Apollo est déclenché. Voici la solution que j'ai implémentée :

import com.ctrip.framework.apollo.model.ConfigChange;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
import org.apache.commons.lang.StringUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import java.util.Map;

@Configuration
@ConfigurationProperties(prefix = "file")
public class CheminFichierProprietes {
    private Map<String, String> cheminMap;

    public Map<String, String> getCheminMap() {
        return cheminMap;
    }

    public void setCheminMap(Map<String, String> cheminMap) {
        this.cheminMap = cheminMap;
    }

    public void ajouter(String cle, String chemin) {
        this.cheminMap.put(cle, chemin);
    }

    public void supprimer(String cle) {
        this.cheminMap.remove(cle);
    }

    @ApolloConfigChangeListener
    private void someChangeHandler(ConfigChangeEvent configChangeEvent) {
        try {
            for (String key : configChangeEvent.changedKeys()) {
                ConfigChange configChange = configChangeEvent.getChange(key);
                System.out.println("found change - " + configChange.toString());
                if (key.startsWith("file")) {
                    String ip = key.substring(key.indexOf('[') + 1, key.indexOf(']'));
                    switch (configChange.getChangeType()) {
                        case ADDED:
                        case MODIFIED:
                            this.ajouter(ip, configChange.getNewValue());
                            break;
                        case DELETED:
                            this.supprimer(ip);
                    }
                }
            }
        } catch (Exception ex) {
            System.err.println("Erreur lors de la mise à jour de la configuration : " + ex.getMessage());
        }
    }
}

Avec cette approche, les modifications apportées aux configurations Apollo sont correctement mises à jour dans la Map configurée via @ConfigurationProperties.

Étiquettes: Apollo ConfigurationProperties Spring Boot Java ConfigChange

Publié le 26 août à 10h34