前言
在前一章中,我们设计了强大的注解API。本章将深入探讨配置管理系统的设计,学习如何将注解中的声明式配置转换为运行时可用的配置对象。
配置管理的核心挑战
在我们的框架中,配置来源有三个层级:

主要挑战:
- 🔀 优先级处理:如何正确合并不同层级的配置
- 🎯 类型安全:确保配置值的类型正确性
- ⚡ 性能优化:避免重复解析和计算
- 🛡️ 配置验证:及早发现配置错误
LogConfig - 统一配置模型
LogConfig
是框架配置管理的核心,承载着从注解解析出来的所有配置信息:
java
package com.simpleflow.log.config;
import com.simpleflow.log.annotation.LogLevel;
/**
* 日志配置类 - 统一管理所有日志相关的配置项
*/
public class LogConfig {
// ========== 基础配置 ==========
private LogLevel level;
private Boolean logArgs;
private Boolean logResult;
private Boolean logExecutionTime;
private Boolean logException;
// ========== 消息模板配置 ==========
private String prefix;
private String startMessage;
private String successMessage;
private String errorMessage;
// ========== 高级配置 ==========
private Boolean enableSpel;
private Boolean includeRequestId;
private int[] excludeArgs;
private String[] sensitiveFields;
// ========== 类级专用配置 ==========
private String[] includeMethods;
private String[] excludeMethods;
private Boolean includePrivateMethods;
private Boolean includeGetterSetter;
/**
* 合并配置 - 当前配置的非空值会覆盖other配置的对应值
*/
public LogConfig merge(LogConfig other) {
if (other == null) {
return new LogConfig(this);
}
LogConfig merged = new LogConfig();
// 基础配置合并(当前配置优先)
merged.level = this.level != null ? this.level : other.level;
merged.logArgs = this.logArgs != null ? this.logArgs : other.logArgs;
merged.logResult = this.logResult != null ? this.logResult : other.logResult;
merged.logExecutionTime = this.logExecutionTime != null ?
this.logExecutionTime : other.logExecutionTime;
merged.logException = this.logException != null ?
this.logException : other.logException;
// 字符串配置合并
merged.prefix = mergeString(this.prefix, other.prefix);
merged.startMessage = mergeString(this.startMessage, other.startMessage);
merged.successMessage = mergeString(this.successMessage, other.successMessage);
merged.errorMessage = mergeString(this.errorMessage, other.errorMessage);
// 高级配置合并
merged.enableSpel = this.enableSpel != null ? this.enableSpel : other.enableSpel;
merged.includeRequestId = this.includeRequestId != null ?
this.includeRequestId : other.includeRequestId;
// 数组配置合并
merged.excludeArgs = mergeIntArray(this.excludeArgs, other.excludeArgs);
merged.sensitiveFields = mergeStringArray(this.sensitiveFields, other.sensitiveFields);
return merged;
}
/**
* 创建默认配置
*/
public static LogConfig createDefault() {
LogConfig config = new LogConfig();
config.level = LogLevel.INFO;
config.logArgs = true;
config.logResult = true;
config.logExecutionTime = true;
config.logException = true;
config.prefix = "";
config.startMessage = "方法开始执行";
config.successMessage = "方法执行成功";
config.errorMessage = "方法执行异常";
config.enableSpel = true;
config.includeRequestId = true;
config.excludeArgs = new int[0];
config.sensitiveFields = new String[0];
config.includeMethods = new String[0];
config.excludeMethods = new String[0];
config.includePrivateMethods = false;
config.includeGetterSetter = false;
return config;
}
// 工具方法
private String mergeString(String current, String other) {
return !isEmpty(current) ? current : other;
}
private boolean isEmpty(String str) {
return str == null || str.trim().isEmpty();
}
// 省略getter/setter和其他方法...
}
AnnotationConfigResolver - 配置解析器
配置解析器负责将注解转换为LogConfig
对象,并处理配置的合并逻辑:
java
package com.simpleflow.log.processor;
import com.simpleflow.log.annotation.*;
import com.simpleflow.log.config.LogConfig;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
/**
* 注解配置解析器
*/
public class AnnotationConfigResolver {
// 配置缓存,提升性能
private final ConcurrentHashMap<Method, LogConfig> methodConfigCache =
new ConcurrentHashMap<>();
private final ConcurrentHashMap<Class<?>, LogConfig> classConfigCache =
new ConcurrentHashMap<>();
private final LogConfig defaultConfig;
public AnnotationConfigResolver() {
this.defaultConfig = LogConfig.createDefault();
}
/**
* 解析方法的完整配置
* 合并优先级:方法配置 > 类配置 > 默认配置
*/
public LogConfig resolveMethodConfig(Method method) {
if (method == null) {
return null;
}
// 检查缓存
LogConfig cached = methodConfigCache.get(method);
if (cached != null) {
return cached;
}
try {
// 1. 检查是否被忽略
if (method.isAnnotationPresent(LogIgnore.class)) {
methodConfigCache.put(method, null);
return null;
}
// 2. 解析方法级配置
LogConfig methodConfig = resolveMethodAnnotation(method);
// 3. 解析类级配置
LogConfig classConfig = resolveClassConfig(method.getDeclaringClass());
// 4. 检查类级配置是否包含此方法
if (classConfig != null && !isMethodIncluded(method, classConfig)) {
methodConfigCache.put(method, null);
return null;
}
// 5. 合并配置:方法 > 类 > 默认
LogConfig finalConfig = mergeConfigs(methodConfig, classConfig, defaultConfig);
// 6. 验证和缓存结果
if (finalConfig != null) {
finalConfig.validate();
}
methodConfigCache.put(method, finalConfig);
return finalConfig;
} catch (Exception e) {
return null;
}
}
/**
* 解析类级配置
*/
public LogConfig resolveClassConfig(Class<?> clazz) {
if (clazz == null) {
return null;
}
LogConfig cached = classConfigCache.get(clazz);
if (cached != null) {
return cached;
}
LogConfig classConfig = null;
if (clazz.isAnnotationPresent(LogClass.class)) {
LogClass logClass = clazz.getAnnotation(LogClass.class);
classConfig = createConfigFromClass(logClass);
}
classConfigCache.put(clazz, classConfig);
return classConfig;
}
/**
* 从@LogMethod注解创建配置
*/
private LogConfig resolveMethodAnnotation(Method method) {
if (!method.isAnnotationPresent(LogMethod.class)) {
return null;
}
LogMethod logMethod = method.getAnnotation(LogMethod.class);
LogConfig config = new LogConfig();
// 基础配置
config.setLevel(logMethod.level());
config.setLogArgs(logMethod.logArgs());
config.setLogResult(logMethod.logResult());
config.setLogExecutionTime(logMethod.logExecutionTime());
config.setLogException(logMethod.logException());
// 消息模板
config.setPrefix(emptyToNull(logMethod.prefix()));
config.setStartMessage(emptyToNull(logMethod.startMessage()));
config.setSuccessMessage(emptyToNull(logMethod.successMessage()));
config.setErrorMessage(emptyToNull(logMethod.errorMessage()));
// 高级配置
config.setEnableSpel(logMethod.enableSpel());
config.setIncludeRequestId(logMethod.includeRequestId());
if (logMethod.excludeArgs().length > 0) {
config.setExcludeArgs(logMethod.excludeArgs());
}
if (logMethod.sensitiveFields().length > 0) {
config.setSensitiveFields(logMethod.sensitiveFields());
}
return config;
}
/**
* 合并多个配置
*/
private LogConfig mergeConfigs(LogConfig methodConfig, LogConfig classConfig,
LogConfig defaultConfig) {
LogConfig result = defaultConfig.copy();
if (classConfig != null) {
result = result.merge(classConfig);
}
if (methodConfig != null) {
result = result.merge(methodConfig);
}
return (methodConfig != null || classConfig != null) ? result : null;
}
// 其他工具方法...
}
配置优先级实战示例
java
// 示例:配置的继承和覆盖
@LogClass(
level = LogLevel.WARN, // 类级默认:WARN级别
logArgs = true, // 类级默认:记录参数
logResult = false, // 类级默认:不记录返回值
prefix = "用户服务" // 类级前缀
)
public class UserService {
// 继承类级配置
public List<User> findAll() {
// 最终配置:level=WARN, logArgs=true, logResult=false, prefix="用户服务"
}
// 部分覆盖类级配置
@LogMethod(logResult = true) // 只覆盖 logResult
public User findById(Long id) {
// 最终配置:level=WARN, logArgs=true, logResult=true, prefix="用户服务"
}
// 完全覆盖类级配置
@LogMethod(
level = LogLevel.DEBUG,
logArgs = false,
prefix = "查询服务"
)
public User findByUsername(String username) {
// 最终配置:level=DEBUG, logArgs=false, logResult=false, prefix="查询服务"
}
// 忽略日志记录
@LogIgnore(reason = "内部工具方法")
private String formatUserInfo(User user) {
// 不会记录任何日志
}
}
测试用例
java
package com.simpleflow.log.processor;
import com.simpleflow.log.annotation.*;
import com.simpleflow.log.config.LogConfig;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import static org.junit.jupiter.api.Assertions.*;
class AnnotationConfigResolverTest {
private final AnnotationConfigResolver resolver = new AnnotationConfigResolver();
@Test
void testMethodConfigOverridesClassConfig() throws Exception {
Method method = TestService.class.getMethod("findById", Long.class);
LogConfig config = resolver.resolveMethodConfig(method);
assertNotNull(config);
assertEquals(LogLevel.INFO, config.getLevel()); // 方法级覆盖
assertTrue(config.getLogResult()); // 方法级覆盖
assertEquals("用户服务", config.getPrefix()); // 继承类级
}
@Test
void testIgnoredMethod() throws Exception {
Method method = TestService.class.getMethod("ignoredMethod");
LogConfig config = resolver.resolveMethodConfig(method);
assertNull(config); // 被忽略的方法返回null
}
@LogClass(level = LogLevel.WARN, prefix = "用户服务", logResult = false)
static class TestService {
@LogMethod(level = LogLevel.INFO, logResult = true)
public User findById(Long id) {
return new User();
}
@LogIgnore(reason = "测试忽略")
public void ignoredMethod() {
}
}
static class User {
}
}
本章小结
✅ 完成的任务
- 设计LogConfig:统一的配置模型,支持合并和验证
- 实现解析器:AnnotationConfigResolver负责注解解析
- 配置优先级:方法 > 类 > 默认的合并策略
- 性能优化:通过缓存提升解析性能
- 测试验证:编写测试用例验证功能
🎯 学习要点
- 配置分层:多层级配置的设计思路
- 合并策略:如何优雅地处理配置优先级
- 缓存机制:提升框架性能的重要手段
- 配置验证:保证配置有效性的机制
💡 思考题
- 为什么要设计配置缓存机制?
- 如何处理配置的循环依赖问题?
- 配置合并还有哪些策略可以考虑?
🚀 下章预告
下一章我们将进入AOP切面编程的世界,学习如何使用Spring AOP实现无侵入式的日志拦截。我们将实现LogMethodAspect和LogClassAspect,掌握切点表达式和环绕通知的使用技巧。
💡 设计原则 : 好的配置管理应该是灵活、高效、易于理解的。通过分层设计和缓存优化,我们既保证了功能的完整性,又确保了良好的性能表现。