配置管理
LCGYL Framework 提供了灵活强大的配置管理系统,支持多种配置源、类型转换、热更新等特性。
核心概念
配置源(PropertySource)
配置源是配置数据的来源,框架支持多种配置源:
- 文件配置:properties、yaml、json
- 环境变量:系统环境变量
- 命令行参数:启动参数
- 远程配置:配置中心(如 Nacos、Apollo)
配置优先级
配置源按照优先级加载,高优先级覆盖低优先级:
命令行参数 > 环境变量 > 配置文件 > 默认值基础使用
1. 创建配置文件
properties
# application.properties
# 应用配置
app.name=LCGYL Application
app.version=1.0.0
app.description=A powerful framework
# 服务器配置
server.host=localhost
server.port=8080
server.contextPath=/api
# 数据库配置
database.url=jdbc:mysql://localhost:3306/lcgyl
database.username=root
database.password=123456
database.pool.maxSize=20
database.pool.minSize=5
# 日志配置
logging.level=INFO
logging.file=/var/log/app.log2. 加载配置
java
import com.lcgyl.core.config.Configuration;
import com.lcgyl.core.config.ConfigurationManager;
public class ConfigExample {
public static void main(String[] args) {
// 加载配置
Configuration config = ConfigurationManager.load("application.properties");
// 读取配置
String appName = config.getString("app.name");
int port = config.getInt("server.port");
boolean debug = config.getBoolean("app.debug", false); // 带默认值
System.out.println("应用名称: " + appName);
System.out.println("端口: " + port);
System.out.println("调试模式: " + debug);
}
}3. 类型转换
java
// 基本类型
String name = config.getString("app.name");
int port = config.getInt("server.port");
long timeout = config.getLong("timeout");
double rate = config.getDouble("rate");
boolean enabled = config.getBoolean("enabled");
// 集合类型
List<String> hosts = config.getList("hosts", String.class);
Set<Integer> ports = config.getSet("ports", Integer.class);
Map<String, String> headers = config.getMap("headers");
// 自定义类型
Duration timeout = config.get("timeout", Duration.class);
LocalDateTime time = config.get("time", LocalDateTime.class);配置绑定
1. 配置类
java
import com.lcgyl.core.config.ConfigurationProperties;
@ConfigurationProperties(prefix = "database")
public class DatabaseConfig {
private String url;
private String username;
private String password;
private PoolConfig pool;
// Getters and Setters
public static class PoolConfig {
private int maxSize = 20;
private int minSize = 5;
private long maxWait = 30000;
// Getters and Setters
}
}2. 绑定配置
java
// 自动绑定
DatabaseConfig dbConfig = ConfigurationManager.bind(
DatabaseConfig.class,
config
);
// 使用配置
System.out.println("数据库URL: " + dbConfig.getUrl());
System.out.println("连接池最大连接数: " + dbConfig.getPool().getMaxSize());3. 嵌套配置
java
@ConfigurationProperties(prefix = "app")
public class AppConfig {
private String name;
private String version;
private ServerConfig server;
private SecurityConfig security;
public static class ServerConfig {
private String host;
private int port;
private String contextPath;
}
public static class SecurityConfig {
private boolean enabled;
private String secretKey;
private List<String> allowedOrigins;
}
}多环境配置
1. 环境配置文件
config/
├── application.properties # 通用配置
├── application-dev.properties # 开发环境
├── application-test.properties # 测试环境
└── application-prod.properties # 生产环境2. 激活环境
java
// 方式1:代码指定
ConfigurationManager.setActiveProfile("prod");
// 方式2:环境变量
// export LCGYL_PROFILE=prod
// 方式3:命令行参数
// java -jar app.jar --profile=prod3. 环境特定配置
properties
# application-prod.properties
database.url=jdbc:mysql://prod-db:3306/lcgyl
database.pool.maxSize=50
logging.level=WARN配置源管理
1. 多配置源
java
import com.lcgyl.core.config.PropertySource;
public class MultiSourceExample {
public static void main(String[] args) {
// 创建配置管理器
ConfigurationManager manager = new ConfigurationManager();
// 添加配置源(按优先级)
manager.addPropertySource(
PropertySource.fromCommandLine(args), // 优先级 1
PropertySource.fromEnvironment(), // 优先级 2
PropertySource.fromFile("application.properties"), // 优先级 3
PropertySource.fromDefaults() // 优先级 4
);
// 获取配置
Configuration config = manager.getConfiguration();
}
}2. 自定义配置源
java
public class DatabasePropertySource implements PropertySource {
private final DataSource dataSource;
public DatabasePropertySource(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public String get(String key) {
try (Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement(
"SELECT value FROM config WHERE key = ?")) {
stmt.setString(1, key);
ResultSet rs = stmt.executeQuery();
return rs.next() ? rs.getString("value") : null;
} catch (SQLException e) {
throw new ConfigurationException("Failed to load config", e);
}
}
@Override
public Map<String, String> getAll() {
// 实现获取所有配置
}
}
// 使用
manager.addPropertySource(new DatabasePropertySource(dataSource));3. 远程配置中心
java
public class NacosPropertySource implements PropertySource {
private final ConfigService configService;
private final String dataId;
private final String group;
public NacosPropertySource(String serverAddr, String dataId, String group) {
try {
Properties properties = new Properties();
properties.put("serverAddr", serverAddr);
this.configService = NacosFactory.createConfigService(properties);
this.dataId = dataId;
this.group = group;
} catch (NacosException e) {
throw new ConfigurationException("Failed to connect to Nacos", e);
}
}
@Override
public String get(String key) {
try {
String content = configService.getConfig(dataId, group, 5000);
Properties props = new Properties();
props.load(new StringReader(content));
return props.getProperty(key);
} catch (Exception e) {
throw new ConfigurationException("Failed to get config", e);
}
}
}配置热更新
1. 监听配置变化
java
import com.lcgyl.core.config.ConfigurationListener;
public class ConfigChangeExample {
public static void main(String[] args) {
Configuration config = ConfigurationManager.load("application.properties");
// 添加监听器
config.addListener(new ConfigurationListener() {
@Override
public void onConfigChanged(String key, String oldValue, String newValue) {
System.out.printf("配置变更: %s = %s -> %s%n",
key, oldValue, newValue);
// 处理配置变更
if ("server.port".equals(key)) {
restartServer(Integer.parseInt(newValue));
}
}
});
// 启用热更新
config.enableHotReload(5000); // 每 5 秒检查一次
}
}2. 配置刷新
java
public class RefreshableConfig {
private final Configuration config;
private volatile int maxConnections;
public RefreshableConfig(Configuration config) {
this.config = config;
this.maxConnections = config.getInt("pool.maxConnections");
// 监听配置变化
config.addListener((key, oldValue, newValue) -> {
if ("pool.maxConnections".equals(key)) {
this.maxConnections = Integer.parseInt(newValue);
System.out.println("连接池大小已更新: " + maxConnections);
}
});
}
public int getMaxConnections() {
return maxConnections;
}
}配置验证
1. 基本验证
java
@ConfigurationProperties(prefix = "server")
public class ServerConfig {
@NotNull
@Min(1024)
@Max(65535)
private Integer port;
@NotBlank
@Pattern(regexp = "^[a-zA-Z0-9.-]+$")
private String host;
@Positive
private int threadPoolSize;
// Getters and Setters
}
// 验证配置
ServerConfig config = ConfigurationManager.bind(ServerConfig.class);
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<ServerConfig>> violations = validator.validate(config);
if (!violations.isEmpty()) {
violations.forEach(v ->
System.err.println(v.getPropertyPath() + ": " + v.getMessage())
);
}2. 自定义验证
java
public class ConfigValidator {
public static void validate(Configuration config) {
// 验证必需配置
requireNonNull(config, "app.name", "应用名称不能为空");
requireNonNull(config, "server.port", "服务端口不能为空");
// 验证端口范围
int port = config.getInt("server.port");
if (port < 1024 || port > 65535) {
throw new ConfigurationException("端口必须在 1024-65535 之间");
}
// 验证数据库URL
String dbUrl = config.getString("database.url");
if (!dbUrl.startsWith("jdbc:")) {
throw new ConfigurationException("无效的数据库URL");
}
}
private static void requireNonNull(Configuration config, String key, String message) {
if (!config.contains(key)) {
throw new ConfigurationException(message);
}
}
}配置加密
1. 加密敏感配置
java
import com.lcgyl.core.config.EncryptionSupport;
public class EncryptedConfigExample {
public static void main(String[] args) {
// 创建加密支持
EncryptionSupport encryption = new EncryptionSupport("my-secret-key");
// 加密配置
String password = "myPassword123";
String encrypted = encryption.encrypt(password);
// 保存到配置文件
// database.password=ENC(encrypted-value)
// 读取时自动解密
Configuration config = ConfigurationManager.load("application.properties");
config.setEncryptionSupport(encryption);
String decrypted = config.getString("database.password");
System.out.println("解密后的密码: " + decrypted);
}
}2. 配置文件示例
properties
# application.properties
# 明文配置
app.name=LCGYL Application
# 加密配置(使用 ENC() 包裹)
database.password=ENC(AES256:base64-encrypted-value)
api.secretKey=ENC(AES256:another-encrypted-value)实战示例
完整的配置管理
java
public class ApplicationConfig {
private static final Logger logger = LoggerFactory.getLogger(ApplicationConfig.class);
public static Configuration loadConfiguration(String[] args) {
// 1. 创建配置管理器
ConfigurationManager manager = new ConfigurationManager();
// 2. 确定环境
String profile = determineProfile(args);
logger.info("激活环境: {}", profile);
// 3. 添加配置源(按优先级)
manager.addPropertySource(
// 命令行参数(最高优先级)
PropertySource.fromCommandLine(args),
// 环境变量
PropertySource.fromEnvironment(),
// 环境特定配置文件
PropertySource.fromFile("application-" + profile + ".properties"),
// 通用配置文件
PropertySource.fromFile("application.properties"),
// 默认配置(最低优先级)
PropertySource.fromDefaults()
);
// 4. 获取配置
Configuration config = manager.getConfiguration();
// 5. 配置加密支持
String encryptionKey = System.getenv("CONFIG_ENCRYPTION_KEY");
if (encryptionKey != null) {
config.setEncryptionSupport(new EncryptionSupport(encryptionKey));
}
// 6. 验证配置
ConfigValidator.validate(config);
// 7. 启用热更新(生产环境)
if ("prod".equals(profile)) {
config.enableHotReload(60000); // 每分钟检查一次
}
// 8. 添加配置监听器
config.addListener((key, oldValue, newValue) -> {
logger.info("配置变更: {} = {} -> {}", key, oldValue, newValue);
});
return config;
}
private static String determineProfile(String[] args) {
// 1. 检查命令行参数
for (String arg : args) {
if (arg.startsWith("--profile=")) {
return arg.substring("--profile=".length());
}
}
// 2. 检查环境变量
String profile = System.getenv("LCGYL_PROFILE");
if (profile != null) {
return profile;
}
// 3. 默认开发环境
return "dev";
}
}最佳实践
1. 配置组织
properties
# ✅ 好的实践:使用分层命名
app.name=MyApp
app.version=1.0.0
server.host=localhost
server.port=8080
server.ssl.enabled=true
server.ssl.keyStore=/path/to/keystore
database.primary.url=jdbc:mysql://localhost/db1
database.primary.username=user1
database.secondary.url=jdbc:mysql://localhost/db2
database.secondary.username=user2
# ❌ 不好的实践:扁平命名
appName=MyApp
serverHost=localhost
databaseUrl=jdbc:mysql://localhost/db2. 默认值
java
// ✅ 好的实践:提供合理的默认值
int port = config.getInt("server.port", 8080);
int threadPoolSize = config.getInt("thread.pool.size",
Runtime.getRuntime().availableProcessors() * 2);
// ❌ 不好的实践:硬编码
int port = 8080; // 无法配置3. 配置文档化
properties
# 服务器配置
# server.host: 服务器主机地址(默认:localhost)
# server.port: 服务器端口(默认:8080,范围:1024-65535)
# server.contextPath: 应用上下文路径(默认:/)
server.host=localhost
server.port=8080
server.contextPath=/api4. 敏感信息保护
properties
# ✅ 好的实践:加密敏感信息
database.password=ENC(encrypted-value)
api.secretKey=ENC(encrypted-value)
# ❌ 不好的实践:明文存储
database.password=myPassword123
api.secretKey=abc123def456注意事项
- 配置优先级:理解配置源的优先级,避免配置被意外覆盖
- 类型安全:使用配置类绑定,而不是字符串键值
- 验证配置:启动时验证配置的完整性和正确性
- 敏感信息:加密存储密码、密钥等敏感信息
- 环境隔离:不同环境使用不同的配置文件
- 热更新谨慎:生产环境的热更新要经过充分测试