Cloud Native 10 min read

Custom Nacos PropertySourceLocator in Spring Cloud Alibaba for Dynamic Config

This article explains how to implement a custom PropertySourceLocator in Spring Cloud Alibaba, detailing the necessary spring.factories configuration, core classes like NacosConfigBootstrapConfiguration and NacosPropertySourceLocator, and the process of obtaining ConfigService, loading shared, extended, and application configurations from Nacos.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Custom Nacos PropertySourceLocator in Spring Cloud Alibaba for Dynamic Config

Environment: Spring Boot 2.3.12.RELEASE, Spring Cloud Alibaba 2.2.5.RELEASE, Spring Cloud Hoxton.SR12.

The core technique is to create a custom PropertySourceLocator and register it via spring.factories.

Configuration is placed in the spring-cloud-context-xxx.jar file with the following entry:

org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration

Nacos loads configuration by default using three DataIds. The main class responsible for this is NacosPropertySourceLocator :

Automatic Configuration Core

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(name = "spring.cloud.nacos.config.enabled", matchIfMissing = true)
public class NacosConfigBootstrapConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public NacosConfigProperties nacosConfigProperties() {
        return new NacosConfigProperties();
    }
    @Bean
    @ConditionalOnMissingBean
    public NacosConfigManager nacosConfigManager(NacosConfigProperties nacosConfigProperties) {
        return new NacosConfigManager(nacosConfigProperties);
    }
    @Bean
    @ConditionalOnMissingBean
    public NacosPropertySourceLocator nacosPropertySourceLocator(NacosConfigManager nacosConfigManager) {
        return new NacosPropertySourceLocator(nacosConfigManager);
    }
}
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.alibaba.cloud.nacos.NacosConfigBootstrapConfiguration

Custom Property Source

public class NacosPropertySourceLocator implements PropertySourceLocator {
    private NacosPropertySourceBuilder nacosPropertySourceBuilder;
    private NacosConfigProperties nacosConfigProperties;
    private NacosConfigManager nacosConfigManager;
    public NacosPropertySourceLocator(NacosConfigManager nacosConfigManager) {
        this.nacosConfigManager = nacosConfigManager;
        this.nacosConfigProperties = nacosConfigManager.getNacosConfigProperties();
    }
    public PropertySource<?> locate(Environment env) {
        nacosConfigProperties.setEnvironment(env);
        ConfigService configService = nacosConfigManager.getConfigService();
        long timeout = nacosConfigProperties.getTimeout();
        nacosPropertySourceBuilder = new NacosPropertySourceBuilder(configService, timeout);
        // load shared, extended, and application configurations
        CompositePropertySource composite = new CompositePropertySource(NACOS_PROPERTY_SOURCE_NAME);
        loadSharedConfiguration(composite);
        loadExtConfiguration(composite);
        loadApplicationConfiguration(composite, dataIdPrefix, nacosConfigProperties, env);
        return composite;
    }
}

Getting Config Service

public interface ConfigService {
    String getConfig(String dataId, String group, long timeoutMs) throws NacosException;
    String getConfigAndSignListener(String dataId, String group, long timeoutMs, Listener listener) throws NacosException;
    void addListener(String dataId, String group, Listener listener) throws NacosException;
    boolean publishConfig(String dataId, String group, String content) throws NacosException;
    boolean removeConfig(String dataId, String group) throws NacosException;
    void removeListener(String dataId, String group, Listener listener);
    String getServerStatus();
    void shutDown() throws NacosException;
}

NacosConfigManager

public class NacosConfigManager {
    private static ConfigService service = null;
    private NacosConfigProperties nacosConfigProperties;
    public NacosConfigManager(NacosConfigProperties nacosConfigProperties) {
        this.nacosConfigProperties = nacosConfigProperties;
        createConfigService(nacosConfigProperties);
    }
    static ConfigService createConfigService(NacosConfigProperties nacosConfigProperties) {
        if (Objects.isNull(service)) {
            synchronized (NacosConfigManager.class) {
                try {
                    if (Objects.isNull(service)) {
                        service = NacosFactory.createConfigService(nacosConfigProperties.assembleConfigServiceProperties());
                    }
                } catch (Throwable e) {
                    throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e);
                }
            }
        }
        return service;
    }
}

Getting Application Configuration

public class NacosPropertySourceLocator implements PropertySourceLocator {
    // ...
    private void loadApplicationConfiguration(CompositePropertySource composite, String dataIdPrefix, NacosConfigProperties properties, Environment env) {
        String fileExtension = properties.getFileExtension();
        String nacosGroup = properties.getGroup();
        loadNacosDataIfPresent(composite, dataIdPrefix + "." + fileExtension, nacosGroup, fileExtension, true);
        for (String profile : env.getActiveProfiles()) {
            String dataId = dataIdPrefix + "-" + profile + "." + fileExtension;
            loadNacosDataIfPresent(composite, dataId, nacosGroup, fileExtension, true);
        }
    }
    // helper methods omitted for brevity
}

Finally, a ConfigService implementation ( NacosConfigService) retrieves remote configuration, falling back to local cache when possible, and continuously retries if the remote service is unavailable, ensuring the application can start only when configuration data is accessible.

Summary: Nacos Config first attempts to read from local cache; if unavailable, it fetches from the remote server, retrying as needed, and only proceeds when configuration data is successfully loaded.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendjavaConfigurationNacosSpring Cloud
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.