/**
* POST TITLE: Nacos Configuration Management in Java
*
* Complete implementation of Nacos for dynamic configuration management in Java applications
*/
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.*;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
public class NacosJavaIntegration {
/**
* Nacos Configuration Manager
*/
public static class NacosConfigManager {
private final String serverAddr;
private final String namespace;
private final ConfigService configService;
private final Map<String, List<ConfigListener>> listeners;
private final ScheduledExecutorService scheduler;
public NacosConfigManager(String serverAddr, String namespace) throws NacosException {
this.serverAddr = serverAddr;
this.namespace = namespace;
this.listeners = new ConcurrentHashMap<>();
this.scheduler = Executors.newScheduledThreadPool(2);
Properties properties = new Properties();
properties.put("serverAddr", serverAddr);
properties.put("namespace", namespace);
this.configService = NacosFactory.createConfigService(properties);
System.out.println("✅ Nacos Config Manager initialized: " + serverAddr);
}
/**
* Configuration Data Class
*/
public static class ConfigData {
private final String dataId;
private final String group;
private final String content;
private final String type;
private final long timestamp;
public ConfigData(String dataId, String group, String content, String type) {
this.dataId = dataId;
this.group = group;
this.content = content;
this.type = type;
this.timestamp = System.currentTimeMillis();
}
// Getters
public String getDataId() { return dataId; }
public String getGroup() { return group; }
public String getContent() { return content; }
public String getType() { return type; }
public long getTimestamp() { return timestamp; }
public <T> T parseAs(Class<T> clazz) {
// Implementation would use Jackson or similar for JSON/YAML parsing
return null; // Simplified
}
}
/**
* Configuration Listener Interface
*/
public interface ConfigListener {
void onConfigChanged(ConfigData newConfig);
void onConfigDeleted(String dataId, String group);
void onError(Exception exception);
}
/**
* Get configuration
*/
public ConfigData getConfig(String dataId, String group) throws NacosException {
return getConfig(dataId, group, 5000);
}
public ConfigData getConfig(String dataId, String group, long timeoutMs) throws NacosException {
String content = configService.getConfig(dataId, group, timeoutMs);
String type = inferConfigType(dataId);
return new ConfigData(dataId, group, content, type);
}
/**
* Get configuration with retry
*/
public ConfigData getConfigWithRetry(String dataId, String group, int maxRetries) throws NacosException {
for (int attempt = 1; attempt <= maxRetries; attempt++) {
try {
return getConfig(dataId, group);
} catch (NacosException e) {
if (attempt == maxRetries) {
throw e;
}
System.out.println("🔄 Retry attempt " + attempt + " for: " + dataId);
try {
Thread.sleep(1000 * attempt);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new NacosException(NacosException.SERVER_ERROR, "Retry interrupted", ie);
}
}
}
throw new NacosException(NacosException.SERVER_ERROR, "All retry attempts failed");
}
/**
* Publish configuration
*/
public boolean publishConfig(String dataId, String group, String content) throws NacosException {
return publishConfig(dataId, group, content, inferConfigType(dataId));
}
public boolean publishConfig(String dataId, String group, String content, String type) throws NacosException {
boolean success = configService.publishConfig(dataId, group, content, type);
if (success) {
System.out.println("✅ Configuration published: " + dataId + " in group: " + group);
} else {
System.out.println("❌ Failed to publish configuration: " + dataId);
}
return success;
}
/**
* Delete configuration
*/
public boolean deleteConfig(String dataId, String group) throws NacosException {
boolean success = configService.removeConfig(dataId, group);
if (success) {
System.out.println("✅ Configuration deleted: " + dataId + " from group: " + group);
notifyConfigDeleted(dataId, group);
}
return success;
}
/**
* Listen for configuration changes
*/
public void addListener(String dataId, String group, ConfigListener listener) throws NacosException {
String key = dataId + "|" + group;
listeners.computeIfAbsent(key, k -> new CopyOnWriteArrayList<>()).add(listener);
configService.addListener(dataId, group, new Listener() {
@Override
public void receiveConfigInfo(String configInfo) {
ConfigData configData = new ConfigData(dataId, group, configInfo, inferConfigType(dataId));
notifyConfigChanged(dataId, group, configData);
}
@Override
public Executor getExecutor() {
return Executors.newSingleThreadExecutor();
}
});
System.out.println("👂 Listening for changes: " + dataId + " in group: " + group);
}
/**
* Remove listener
*/
public void removeListener(String dataId, String group, ConfigListener listener) {
String key = dataId + "|" + group;
List<ConfigListener> listenerList = listeners.get(key);
if (listenerList != null) {
listenerList.remove(listener);
if (listenerList.isEmpty()) {
listeners.remove(key);
}
}
}
/**
* Batch get configurations
*/
public Map<String, ConfigData> getConfigs(List<String> dataIds, String group) throws NacosException {
Map<String, ConfigData> configs = new HashMap<>();
for (String dataId : dataIds) {
try {
ConfigData config = getConfig(dataId, group);
configs.put(dataId, config);
} catch (NacosException e) {
System.err.println("❌ Failed to get config: " + dataId + " - " + e.getMessage());
}
}
return configs;
}
/**
* Batch publish configurations
*/
public Map<String, Boolean> publishConfigs(Map<String, String> configs, String group) throws NacosException {
Map<String, Boolean> results = new HashMap<>();
for (Map.Entry<String, String> entry : configs.entrySet()) {
try {
boolean success = publishConfig(entry.getKey(), group, entry.getValue());
results.put(entry.getKey(), success);
} catch (NacosException e) {
results.put(entry.getKey(), false);
System.err.println("❌ Failed to publish config: " + entry.getKey() + " - " + e.getMessage());
}
}
return results;
}
private String inferConfigType(String dataId) {
if (dataId.endsWith(".yaml") || dataId.endsWith(".yml")) {
return "yaml";
} else if (dataId.endsWith(".properties")) {
return "properties";
} else if (dataId.endsWith(".json")) {
return "json";
} else if (dataId.endsWith(".xml")) {
return "xml";
}
return "text";
}
private void notifyConfigChanged(String dataId, String group, ConfigData newConfig) {
String key = dataId + "|" + group;
List<ConfigListener> listenerList = listeners.get(key);
if (listenerList != null) {
for (ConfigListener listener : listenerList) {
try {
listener.onConfigChanged(newConfig);
} catch (Exception e) {
System.err.println("❌ Error in config listener: " + e.getMessage());
listener.onError(e);
}
}
}
}
private void notifyConfigDeleted(String dataId, String group) {
String key = dataId + "|" + group;
List<ConfigListener> listenerList = listeners.get(key);
if (listenerList != null) {
for (ConfigListener listener : listenerList) {
try {
listener.onConfigDeleted(dataId, group);
} catch (Exception e) {
System.err.println("❌ Error in config listener: " + e.getMessage());
listener.onError(e);
}
}
}
}
/**
* Health check
*/
public boolean healthCheck() {
try {
// Try to get server status
configService.getServerStatus();
return true;
} catch (NacosException e) {
return false;
}
}
/**
* Shutdown
*/
public void shutdown() {
try {
configService.shutDown();
scheduler.shutdown();
if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
scheduler.shutdownNow();
}
} catch (Exception e) {
System.err.println("❌ Error shutting down Nacos config manager: " + e.getMessage());
}
}
}
/**
* Configuration Properties Manager
*/
public static class ConfigPropertiesManager {
private final NacosConfigManager configManager;
private final Map<String, Properties> cachedProperties;
private final ScheduledExecutorService refreshScheduler;
public ConfigPropertiesManager(NacosConfigManager configManager) {
this.configManager = configManager;
this.cachedProperties = new ConcurrentHashMap<>();
this.refreshScheduler = Executors.newScheduledThreadPool(1);
startAutoRefresh();
}
/**
* Get properties from Nacos
*/
public Properties getProperties(String dataId, String group) throws NacosException {
String cacheKey = dataId + "|" + group;
Properties props = cachedProperties.get(cacheKey);
if (props == null) {
props = loadPropertiesFromNacos(dataId, group);
cachedProperties.put(cacheKey, props);
// Register for changes
configManager.addListener(dataId, group, new NacosConfigManager.ConfigListener() {
@Override
public void onConfigChanged(NacosConfigManager.ConfigData newConfig) {
try {
Properties newProps = parseProperties(newConfig.getContent(), newConfig.getType());
cachedProperties.put(cacheKey, newProps);
System.out.println("🔄 Properties updated: " + dataId);
} catch (Exception e) {
System.err.println("❌ Failed to parse updated properties: " + e.getMessage());
}
}
@Override
public void onConfigDeleted(String deletedDataId, String deletedGroup) {
cachedProperties.remove(cacheKey);
System.out.println("🗑️ Properties removed from cache: " + deletedDataId);
}
@Override
public void onError(Exception exception) {
System.err.println("❌ Error in properties listener: " + exception.getMessage());
}
});
}
return props;
}
/**
* Get property value
*/
public String getProperty(String dataId, String group, String key) throws NacosException {
Properties props = getProperties(dataId, group);
return props.getProperty(key);
}
public String getProperty(String dataId, String group, String key, String defaultValue) throws NacosException {
String value = getProperty(dataId, group, key);
return value != null ? value : defaultValue;
}
/**
* Get integer property
*/
public Integer getIntProperty(String dataId, String group, String key) throws NacosException {
String value = getProperty(dataId, group, key);
return value != null ? Integer.parseInt(value) : null;
}
public int getIntProperty(String dataId, String group, String key, int defaultValue) throws NacosException {
Integer value = getIntProperty(dataId, group, key);
return value != null ? value : defaultValue;
}
/**
* Get boolean property
*/
public Boolean getBooleanProperty(String dataId, String group, String key) throws NacosException {
String value = getProperty(dataId, group, key);
return value != null ? Boolean.parseBoolean(value) : null;
}
public boolean getBooleanProperty(String dataId, String group, String key, boolean defaultValue) throws NacosException {
Boolean value = getBooleanProperty(dataId, group, key);
return value != null ? value : defaultValue;
}
private Properties loadPropertiesFromNacos(String dataId, String group) throws NacosException {
NacosConfigManager.ConfigData config = configManager.getConfig(dataId, group);
return parseProperties(config.getContent(), config.getType());
}
private Properties parseProperties(String content, String type) {
Properties props = new Properties();
try {
if ("properties".equals(type)) {
props.load(new StringReader(content));
} else if ("yaml".equals(type) || "yml".equals(type)) {
// For YAML, we'd use SnakeYAML or similar
// This is a simplified implementation
parseYamlToProperties(content, props);
} else {
// For other types, treat as key=value
props.load(new StringReader(content));
}
} catch (Exception e) {
System.err.println("❌ Failed to parse properties: " + e.getMessage());
}
return props;
}
private void parseYamlToProperties(String yamlContent, Properties props) {
// Simplified YAML parsing - in production use SnakeYAML
String[] lines = yamlContent.split("\n");
for (String line : lines) {
line = line.trim();
if (!line.isEmpty() && !line.startsWith("#") && line.contains(":")) {
String[] parts = line.split(":", 2);
if (parts.length == 2) {
String key = parts[0].trim();
String value = parts[1].trim();
props.setProperty(key, value);
}
}
}
}
private void startAutoRefresh() {
// Refresh all cached properties every 5 minutes
refreshScheduler.scheduleAtFixedRate(() -> {
try {
refreshAllProperties();
} catch (Exception e) {
System.err.println("❌ Auto-refresh failed: " + e.getMessage());
}
}, 5, 5, TimeUnit.MINUTES);
}
private void refreshAllProperties() throws NacosException {
for (String cacheKey : cachedProperties.keySet()) {
String[] parts = cacheKey.split("\\|");
if (parts.length == 2) {
String dataId = parts[0];
String group = parts[1];
try {
Properties freshProps = loadPropertiesFromNacos(dataId, group);
cachedProperties.put(cacheKey, freshProps);
} catch (NacosException e) {
System.err.println("❌ Failed to refresh properties: " + dataId);
}
}
}
}
/**
* Clear cache
*/
public void clearCache() {
cachedProperties.clear();
System.out.println("🧹 Properties cache cleared");
}
/**
* Shutdown
*/
public void shutdown() {
refreshScheduler.shutdown();
try {
if (!refreshScheduler.awaitTermination(5, TimeUnit.SECONDS)) {
refreshScheduler.shutdownNow();
}
} catch (InterruptedException e) {
refreshScheduler.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
/**
* Spring Boot Integration
*/
@Configuration
@ConfigurationProperties(prefix = "nacos.config")
public static class NacosConfigProperties {
private String serverAddr = "localhost:8848";
private String namespace = "";
private String group = "DEFAULT_GROUP";
private String fileExtension = "properties";
private int timeout = 3000;
private boolean autoRefresh = true;
// Getters and setters
public String getServerAddr() { return serverAddr; }
public void setServerAddr(String serverAddr) { this.serverAddr = serverAddr; }
public String getNamespace() { return namespace; }
public void setNamespace(String namespace) { this.namespace = namespace; }
public String getGroup() { return group; }
public void setGroup(String group) { this.group = group; }
public String getFileExtension() { return fileExtension; }
public void setFileExtension(String fileExtension) { this.fileExtension = fileExtension; }
public int getTimeout() { return timeout; }
public void setTimeout(int timeout) { this.timeout = timeout; }
public boolean isAutoRefresh() { return autoRefresh; }
public void setAutoRefresh(boolean autoRefresh) { this.autoRefresh = autoRefresh; }
}
@SpringBootApplication
public static class NacosIntegrationApplication {
@Bean
public NacosConfigManager nacosConfigManager(NacosConfigProperties properties) throws NacosException {
return new NacosConfigManager(properties.getServerAddr(), properties.getNamespace());
}
@Bean
public ConfigPropertiesManager configPropertiesManager(NacosConfigManager configManager) {
return new ConfigPropertiesManager(configManager);
}
@Bean
@RefreshScope
public ApplicationConfig applicationConfig(ConfigPropertiesManager propertiesManager,
NacosConfigProperties nacosProperties) throws NacosException {
return new ApplicationConfig(propertiesManager, nacosProperties.getGroup());
}
}
@RefreshScope
@Component
public static class ApplicationConfig {
private final ConfigPropertiesManager propertiesManager;
private final String group;
public ApplicationConfig(ConfigPropertiesManager propertiesManager, String group) {
this.propertiesManager = propertiesManager;
this.group = group;
}
@Value("${app.name:${spring.application.name:unknown}}")
private String appName;
public String getDatabaseUrl() {
try {
return propertiesManager.getProperty("datasource.properties", group, "url");
} catch (Exception e) {
return "jdbc:mysql://localhost:3306/mydb";
}
}
public String getDatabaseUsername() {
try {
return propertiesManager.getProperty("datasource.properties", group, "username");
} catch (Exception e) {
return "root";
}
}
public String getDatabasePassword() {
try {
return propertiesManager.getProperty("datasource.properties", group, "password");
} catch (Exception e) {
return "password";
}
}
public int getServerPort() {
try {
return propertiesManager.getIntProperty("server.properties", group, "port", 8080);
} catch (Exception e) {
return 8080;
}
}
public boolean isFeatureEnabled(String featureName) {
try {
return propertiesManager.getBooleanProperty("features.properties", group, featureName, false);
} catch (Exception e) {
return false;
}
}
public Map<String, String> getExternalServices() {
try {
Properties props = propertiesManager.getProperties("services.properties", group);
Map<String, String> services = new HashMap<>();
for (String key : props.stringPropertyNames()) {
if (key.startsWith("service.")) {
String serviceName = key.substring(8); // Remove "service." prefix
services.put(serviceName, props.getProperty(key));
}
}
return services;
} catch (Exception e) {
return Collections.emptyMap();
}
}
}
@RestController
@RequestMapping("/api/config")
public static class ConfigController {
private final NacosConfigManager configManager;
private final ConfigPropertiesManager propertiesManager;
private final ApplicationConfig applicationConfig;
private final NacosConfigProperties nacosProperties;
public ConfigController(NacosConfigManager configManager,
ConfigPropertiesManager propertiesManager,
ApplicationConfig applicationConfig,
NacosConfigProperties nacosProperties) {
this.configManager = configManager;
this.propertiesManager = propertiesManager;
this.applicationConfig = applicationConfig;
this.nacosProperties = nacosProperties;
}
@GetMapping("/{dataId}")
public ResponseEntity<?> getConfig(@PathVariable String dataId,
@RequestParam(defaultValue = "DEFAULT_GROUP") String group) {
try {
NacosConfigManager.ConfigData config = configManager.getConfig(dataId, group);
Map<String, Object> response = new HashMap<>();
response.put("dataId", config.getDataId());
response.put("group", config.getGroup());
response.put("type", config.getType());
response.put("timestamp", config.getTimestamp());
response.put("content", config.getContent());
return ResponseEntity.ok(response);
} catch (NacosException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(Map.of("error", "Failed to get config: " + e.getMessage()));
}
}
@PostMapping("/{dataId}")
public ResponseEntity<String> publishConfig(@PathVariable String dataId,
@RequestParam(defaultValue = "DEFAULT_GROUP") String group,
@RequestBody Map<String, String> request) {
try {
String content = request.get("content");
String type = request.getOrDefault("type", inferTypeFromDataId(dataId));
boolean success = configManager.publishConfig(dataId, group, content, type);
if (success) {
return ResponseEntity.ok("Configuration published successfully");
} else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Failed to publish configuration");
}
} catch (NacosException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Failed to publish config: " + e.getMessage());
}
}
@DeleteMapping("/{dataId}")
public ResponseEntity<String> deleteConfig(@PathVariable String dataId,
@RequestParam(defaultValue = "DEFAULT_GROUP") String group) {
try {
boolean success = configManager.deleteConfig(dataId, group);
if (success) {
return ResponseEntity.ok("Configuration deleted successfully");
} else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Failed to delete configuration");
}
} catch (NacosException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Failed to delete config: " + e.getMessage());
}
}
@GetMapping("/properties/{dataId}")
public ResponseEntity<?> getProperties(@PathVariable String dataId,
@RequestParam(defaultValue = "DEFAULT_GROUP") String group) {
try {
Properties props = propertiesManager.getProperties(dataId, group);
return ResponseEntity.ok(props);
} catch (NacosException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(Map.of("error", "Failed to get properties: " + e.getMessage()));
}
}
@GetMapping("/app/config")
public ResponseEntity<Map<String, Object>> getApplicationConfig() {
Map<String, Object> config = new HashMap<>();
config.put("databaseUrl", applicationConfig.getDatabaseUrl());
config.put("databaseUsername", applicationConfig.getDatabaseUsername());
config.put("serverPort", applicationConfig.getServerPort());
config.put("externalServices", applicationConfig.getExternalServices());
// Check some feature flags
config.put("featureAEnabled", applicationConfig.isFeatureEnabled("featureA"));
config.put("featureBEnabled", applicationConfig.isFeatureEnabled("featureB"));
return ResponseEntity.ok(config);
}
@PostMapping("/cache/clear")
public ResponseEntity<String> clearCache() {
propertiesManager.clearCache();
return ResponseEntity.ok("Cache cleared successfully");
}
@GetMapping("/health")
public ResponseEntity<Map<String, Object>> healthCheck() {
Map<String, Object> health = new HashMap<>();
health.put("status", configManager.healthCheck() ? "UP" : "DOWN");
health.put("server", nacosProperties.getServerAddr());
health.put("namespace", nacosProperties.getNamespace());
health.put("timestamp", new Date());
return ResponseEntity.ok(health);
}
private String inferTypeFromDataId(String dataId) {
if (dataId.endsWith(".yaml") || dataId.endsWith(".yml")) return "yaml";
if (dataId.endsWith(".properties")) return "properties";
if (dataId.endsWith(".json")) return "json";
if (dataId.endsWith(".xml")) return "xml";
return "text";
}
}
/**
* Configuration Examples Generator
*/
public static class ConfigExamplesGenerator {
private final NacosConfigManager configManager;
public ConfigExamplesGenerator(NacosConfigManager configManager) {
this.configManager = configManager;
}
/**
* Generate sample configurations for a microservices application
*/
public void generateSampleConfigurations() throws NacosException {
// Database configuration
String datasourceConfig = """
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=admin
spring.datasource.password=secret123
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
""";
configManager.publishConfig("datasource.properties", "DEFAULT_GROUP", datasourceConfig);
// Server configuration
String serverConfig = """
server.port=8080
server.servlet.context-path=/api
spring.mvc.format.date=yyyy-MM-dd
logging.level.com.example=DEBUG
""";
configManager.publishConfig("server.properties", "DEFAULT_GROUP", serverConfig);
// Feature flags
String featuresConfig = """
feature.payment.enabled=true
feature.notification.enabled=false
feature.analytics.enabled=true
feature.experimental.ui=false
""";
configManager.publishConfig("features.properties", "DEFAULT_GROUP", featuresConfig);
// External services
String servicesConfig = """
service.payment.url=https://payment-service:8081
service.user.url=https://user-service:8082
service.product.url=https://product-service:8083
service.notification.url=https://notification-service:8084
""";
configManager.publishConfig("services.properties", "DEFAULT_GROUP", servicesConfig);
// YAML configuration example
String applicationYaml = """
app:
name: order-service
version: 1.0.0
environment: development
database:
primary:
host: localhost
port: 5432
name: order_db
replica:
host: replica-host
port: 5432
name: order_db_replica
cache:
redis:
host: redis-host
port: 6379
timeout: 5000
""";
configManager.publishConfig("application.yaml", "DEFAULT_GROUP", applicationYaml, "yaml");
System.out.println("✅ Sample configurations generated successfully");
}
/**
* Generate configuration for specific service
*/
public void generateServiceConfig(String serviceName, int port, String databaseUrl) throws NacosException {
String serviceConfig = String.format("""
spring.application.name=%s
server.port=%d
spring.datasource.url=%s
spring.datasource.username=service_user
spring.datasource.password=service_pass_123
logging.level.com.%s=INFO
management.endpoints.web.exposure.include=health,info,metrics
""", serviceName, port, databaseUrl, serviceName);
String dataId = serviceName + ".properties";
configManager.publishConfig(dataId, "DEFAULT_GROUP", serviceConfig);
System.out.println("✅ Service configuration generated: " + dataId);
}
}
/**
* Demo and Usage Examples
*/
public static void main(String[] args) {
System.out.println("☁️ Nacos Configuration Management in Java");
System.out.println("==========================================\n");
try {
// Demo 1: Initialize Nacos Config Manager
System.out.println("1. ⚙️ Initializing Nacos Config Manager");
NacosConfigManager configManager = new NacosConfigManager("localhost:8848", "");
System.out.println(" Nacos server: localhost:8848");
System.out.println(" Health check: " + (configManager.healthCheck() ? "✅" : "❌"));
// Demo 2: Configuration Operations
System.out.println("\n2. 📝 Configuration Operations");
ConfigExamplesGenerator examplesGenerator = new ConfigExamplesGenerator(configManager);
examplesGenerator.generateSampleConfigurations();
// Demo 3: Properties Management
System.out.println("\n3. 🗂️ Properties Management");
ConfigPropertiesManager propsManager = new ConfigPropertiesManager(configManager);
Properties datasourceProps = propsManager.getProperties("datasource.properties", "DEFAULT_GROUP");
System.out.println(" Database URL: " + datasourceProps.getProperty("spring.datasource.url"));
System.out.println(" Database User: " + datasourceProps.getProperty("spring.datasource.username"));
// Demo 4: Configuration Listening
System.out.println("\n4. 👂 Configuration Change Listening");
configManager.addListener("features.properties", "DEFAULT_GROUP",
new NacosConfigManager.ConfigListener() {
@Override
public void onConfigChanged(NacosConfigManager.ConfigData newConfig) {
System.out.println(" 🔄 Features configuration changed!");
System.out.println(" New content: " + newConfig.getContent().substring(0, 50) + "...");
}
@Override
public void onConfigDeleted(String dataId, String group) {
System.out.println(" 🗑️ Configuration deleted: " + dataId);
}
@Override
public void onError(Exception exception) {
System.err.println(" ❌ Listener error: " + exception.getMessage());
}
});
System.out.println(" Listening for changes to features.properties");
// Demo 5: Spring-like Configuration
System.out.println("\n5. 🌱 Spring-like Configuration Management");
ApplicationConfig appConfig = new ApplicationConfig(propsManager, "DEFAULT_GROUP");
System.out.println(" Database URL: " + appConfig.getDatabaseUrl());
System.out.println(" Server Port: " + appConfig.getServerPort());
// Demo 6: Cleanup
System.out.println("\n6. 🧹 Cleanup");
propsManager.shutdown();
configManager.shutdown();
System.out.println("\n✅ Nacos Configuration Demo Completed");
System.out.println("\n📚 Key Features:");
System.out.println(" - Dynamic configuration management");
System.out.println(" - Real-time configuration updates");
System.out.println(" - Multiple configuration formats (Properties, YAML, JSON)");
System.out.println(" - Spring Boot integration with @RefreshScope");
System.out.println(" - Configuration change listeners");
System.out.println(" - Health monitoring and auto-refresh");
System.out.println(" - Batch operations for efficiency");
} catch (Exception e) {
System.err.println("❌ Demo failed: " + e.getMessage());
e.printStackTrace();
}
}
}
Maven Dependencies
<!-- pom.xml --> <dependencies> <!-- Nacos Client --> <dependency> <groupId>com.alibaba.nacos</groupId> <artifactId>nacos-client</artifactId> <version>2.2.3</version> </dependency> <!-- Spring Cloud Alibaba Nacos Config --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <version>2022.0.0.0</version> </dependency> <!-- Spring Boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> <version>2.7.0</version> </dependency> <!-- Spring Cloud Context (for @RefreshScope) --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-context</artifactId> <version>3.1.5</version> </dependency> </dependencies>
Configuration Examples
application.yml (Spring Boot)
spring: application: name: order-service cloud: nacos: config: server-addr: localhost:8848 namespace: dev group: DEFAULT_GROUP file-extension: yaml refresh-enabled: true config: import: optional:nacos:application.yaml server: port: 8080 management: endpoints: web: exposure: include: health,info,metrics,refresh
Nacos Configuration Examples
application.yaml in Nacos
app:
name: order-service
version: 1.0.0
environment: production
database:
primary:
host: db-primary.example.com
port: 5432
name: order_db
replica:
host: db-replica.example.com
port: 5432
name: order_db_replica
redis:
host: redis-cluster.example.com
port: 6379
password: ${REDIS_PASSWORD}
timeout: 5000
features:
payment-enabled: true
notification-enabled: false
analytics-enabled: true
datasource.properties in Nacos
spring.datasource.url=jdbc:postgresql://db-primary.example.com:5432/order_db
spring.datasource.username=order_service
spring.datasource.password=${DB_PASSWORD}
spring.datasource.driver-class-name=org.postgresql.Driver
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.show-sql=false
features.properties in Nacos
feature.payment.enabled=true feature.notification.enabled=false feature.analytics.enabled=true feature.experimental.ui=false feature.rate.limiting.enabled=true
Usage Examples
// Initialize Nacos config manager
NacosConfigManager configManager = new NacosConfigManager("localhost:8848", "dev");
// Get configuration
NacosConfigManager.ConfigData config = configManager.getConfig("application.yaml", "DEFAULT_GROUP");
System.out.println("Config content: " + config.getContent());
// Listen for changes
configManager.addListener("application.yaml", "DEFAULT_GROUP",
new NacosConfigManager.ConfigListener() {
@Override
public void onConfigChanged(NacosConfigManager.ConfigData newConfig) {
System.out.println("Configuration updated!");
// Reload application configuration
reloadApplicationConfig();
}
@Override
public void onConfigDeleted(String dataId, String group) {
System.out.println("Configuration deleted: " + dataId);
}
@Override
public void onError(Exception exception) {
System.err.println("Error in config listener: " + exception.getMessage());
}
});
// Use with Spring Boot
@RefreshScope
@Component
public class DatabaseConfig {
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
// Configuration will be automatically refreshed when changed in Nacos
}
This Nacos configuration management implementation provides:
- Complete Nacos Integration with dynamic configuration management
- Multiple Configuration Formats support (Properties, YAML, JSON, XML)
- Real-time Configuration Updates with listener pattern
- Spring Boot Integration with @RefreshScope support
- Configuration Caching with automatic refresh
- Health Monitoring and connection management
- Batch Operations for efficient configuration handling
- Error Handling with retry mechanisms
- Namespace and Group Management for multi-environment support
- REST API for configuration management
The solution enables Java applications to leverage Nacos for centralized configuration management with dynamic updates and Spring Boot integration for seamless adoption.