前言
前面我们实现了日志框架的核心功能,本章将创建Spring Boot Starter,让框架具备自动配置能力,实现开箱即用的用户体验。这是框架走向生产级应用的重要一步。
Spring Boot自动配置原理
自动配置的工作机制

核心概念:
- 📦 spring.factories: 自动配置的入口文件
- ⚙️ AutoConfiguration: 自动配置类
- 🎯 @Conditional: 条件装配注解
- 🔧 ConfigurationProperties: 配置属性绑定
LogProperties - 配置属性类
java
package com.simpleflow.log.springboot.properties;
import com.simpleflow.log.annotation.LogLevel;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 日志框架配置属性
*
* 绑定application.yml中的simpleflow.log配置
*/
@ConfigurationProperties(prefix = "simpleflow.log")
public class LogProperties {
/**
* 是否启用日志框架
*/
private boolean enabled = true;
/**
* 默认日志级别
*/
private LogLevel defaultLevel = LogLevel.INFO;
/**
* 是否启用Web集成
*/
private boolean webEnabled = true;
/**
* 是否启用Actuator集成
*/
private boolean actuatorEnabled = true;
/**
* 是否记录方法参数
*/
private boolean logArgs = true;
/**
* 是否记录方法返回值
*/
private boolean logResult = true;
/**
* 是否记录执行时间
*/
private boolean logExecutionTime = true;
/**
* 是否记录异常信息
*/
private boolean logException = true;
/**
* 全局敏感字段配置
*/
private String[] globalSensitiveFields = {
"password", "pwd", "secret", "token", "key"
};
/**
* 请求日志配置
*/
private RequestLog requestLog = new RequestLog();
/**
* 性能配置
*/
private Performance performance = new Performance();
// ========== 内部配置类 ==========
/**
* 请求日志配置
*/
public static class RequestLog {
/**
* 是否启用请求日志
*/
private boolean enabled = true;
/**
* 是否记录请求头
*/
private boolean logHeaders = false;
/**
* 是否记录请求参数
*/
private boolean logParameters = true;
/**
* 是否记录响应体
*/
private boolean logResponseBody = false;
/**
* 排除的URL模式
*/
private String[] excludePatterns = {
"/health", "/actuator/**", "/favicon.ico"
};
// getters and setters...
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public boolean isLogHeaders() { return logHeaders; }
public void setLogHeaders(boolean logHeaders) { this.logHeaders = logHeaders; }
public boolean isLogParameters() { return logParameters; }
public void setLogParameters(boolean logParameters) { this.logParameters = logParameters; }
public boolean isLogResponseBody() { return logResponseBody; }
public void setLogResponseBody(boolean logResponseBody) { this.logResponseBody = logResponseBody; }
public String[] getExcludePatterns() { return excludePatterns; }
public void setExcludePatterns(String[] excludePatterns) { this.excludePatterns = excludePatterns; }
}
/**
* 性能配置
*/
public static class Performance {
/**
* 是否启用异步日志
*/
private boolean asyncEnabled = false;
/**
* 异步队列大小
*/
private int asyncQueueSize = 1000;
/**
* 日志缓存大小
*/
private int cacheSize = 500;
/**
* 最大日志长度
*/
private int maxLogLength = 10000;
// getters and setters...
public boolean isAsyncEnabled() { return asyncEnabled; }
public void setAsyncEnabled(boolean asyncEnabled) { this.asyncEnabled = asyncEnabled; }
public int getAsyncQueueSize() { return asyncQueueSize; }
public void setAsyncQueueSize(int asyncQueueSize) { this.asyncQueueSize = asyncQueueSize; }
public int getCacheSize() { return cacheSize; }
public void setCacheSize(int cacheSize) { this.cacheSize = cacheSize; }
public int getMaxLogLength() { return maxLogLength; }
public void setMaxLogLength(int maxLogLength) { this.maxLogLength = maxLogLength; }
}
// ========== Getters and Setters ==========
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public LogLevel getDefaultLevel() { return defaultLevel; }
public void setDefaultLevel(LogLevel defaultLevel) { this.defaultLevel = defaultLevel; }
public boolean isWebEnabled() { return webEnabled; }
public void setWebEnabled(boolean webEnabled) { this.webEnabled = webEnabled; }
public boolean isActuatorEnabled() { return actuatorEnabled; }
public void setActuatorEnabled(boolean actuatorEnabled) { this.actuatorEnabled = actuatorEnabled; }
public boolean isLogArgs() { return logArgs; }
public void setLogArgs(boolean logArgs) { this.logArgs = logArgs; }
public boolean isLogResult() { return logResult; }
public void setLogResult(boolean logResult) { this.logResult = logResult; }
public boolean isLogExecutionTime() { return logExecutionTime; }
public void setLogExecutionTime(boolean logExecutionTime) { this.logExecutionTime = logExecutionTime; }
public boolean isLogException() { return logException; }
public void setLogException(boolean logException) { this.logException = logException; }
public String[] getGlobalSensitiveFields() { return globalSensitiveFields; }
public void setGlobalSensitiveFields(String[] globalSensitiveFields) { this.globalSensitiveFields = globalSensitiveFields; }
public RequestLog getRequestLog() { return requestLog; }
public void setRequestLog(RequestLog requestLog) { this.requestLog = requestLog; }
public Performance getPerformance() { return performance; }
public void setPerformance(Performance performance) { this.performance = performance; }
}
ConditionalOnLogEnabled - 条件装配注解
java
package com.simpleflow.log.springboot.condition;
import org.springframework.context.annotation.Conditional;
import java.lang.annotation.*;
/**
* 条件装配注解 - 仅在日志框架启用时生效
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnLogEnabledCondition.class)
public @interface ConditionalOnLogEnabled {
}
java
package com.simpleflow.log.springboot.condition;
import com.simpleflow.log.springboot.properties.LogProperties;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* 日志启用条件判断
*/
public class OnLogEnabledCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage
.forCondition("SimpleFlow Log Enabled");
try {
// 绑定配置属性
Binder binder = Binder.get(context.getEnvironment());
LogProperties properties = binder
.bind("simpleflow.log", Bindable.of(LogProperties.class))
.orElse(new LogProperties());
if (properties.isEnabled()) {
return ConditionOutcome.match(message.found("enabled", true));
} else {
return ConditionOutcome.noMatch(message.found("enabled", false));
}
} catch (Exception e) {
return ConditionOutcome.noMatch(message.because("配置绑定失败: " + e.getMessage()));
}
}
}
LogAutoConfiguration - 自动配置类
java
package com.simpleflow.log.springboot.autoconfigure;
import com.simpleflow.log.config.LogConfig;
import com.simpleflow.log.formatter.DefaultLogFormatter;
import com.simpleflow.log.formatter.LogFormatter;
import com.simpleflow.log.processor.AnnotationConfigResolver;
import com.simpleflow.log.processor.LogMethodProcessor;
import com.simpleflow.log.springboot.condition.ConditionalOnLogEnabled;
import com.simpleflow.log.springboot.properties.LogProperties;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
/**
* 日志框架自动配置类
*/
@AutoConfiguration
@ConditionalOnLogEnabled
@EnableConfigurationProperties(LogProperties.class)
@ComponentScan(basePackages = {
"com.simpleflow.log.aop",
"com.simpleflow.log.processor"
})
public class LogAutoConfiguration {
/**
* 默认配置Bean
*/
@Bean
@ConditionalOnMissingBean
public LogConfig defaultLogConfig(LogProperties properties) {
LogConfig config = LogConfig.createDefault();
// 应用全局配置
config.setLevel(properties.getDefaultLevel());
config.setLogArgs(properties.isLogArgs());
config.setLogResult(properties.isLogResult());
config.setLogExecutionTime(properties.isLogExecutionTime());
config.setLogException(properties.isLogException());
config.setSensitiveFields(properties.getGlobalSensitiveFields());
return config;
}
/**
* 配置解析器Bean
*/
@Bean
@ConditionalOnMissingBean
public AnnotationConfigResolver annotationConfigResolver(LogConfig defaultLogConfig) {
return new AnnotationConfigResolver(defaultLogConfig);
}
/**
* 日志格式化器Bean
*/
@Bean
@ConditionalOnMissingBean
public LogFormatter logFormatter() {
return new DefaultLogFormatter();
}
/**
* 日志方法处理器Bean
*/
@Bean
@ConditionalOnMissingBean
public LogMethodProcessor logMethodProcessor(LogFormatter logFormatter) {
return new LogMethodProcessor(logFormatter);
}
}
LogWebAutoConfiguration - Web自动配置
java
package com.simpleflow.log.springboot.autoconfigure;
import com.simpleflow.log.springboot.condition.ConditionalOnLogEnabled;
import com.simpleflow.log.springboot.properties.LogProperties;
import com.simpleflow.log.web.config.WebLogConfiguration;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.DispatcherServlet;
/**
* Web环境自动配置
*/
@AutoConfiguration(after = LogAutoConfiguration.class)
@ConditionalOnLogEnabled
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@ConditionalOnProperty(
prefix = "simpleflow.log",
name = "web-enabled",
havingValue = "true",
matchIfMissing = true
)
@ComponentScan(basePackages = "com.simpleflow.log.web")
@Import(WebLogConfiguration.class)
public class LogWebAutoConfiguration {
// Web相关的自动配置
// Filter和Interceptor由@ComponentScan自动注册
}
LogActuatorAutoConfiguration - 监控集成
java
package com.simpleflow.log.springboot.autoconfigure;
import com.simpleflow.log.springboot.actuator.LogEndpoint;
import com.simpleflow.log.springboot.actuator.LogHealthIndicator;
import com.simpleflow.log.springboot.condition.ConditionalOnLogEnabled;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
/**
* Actuator集成自动配置
*/
@AutoConfiguration(after = LogAutoConfiguration.class)
@ConditionalOnLogEnabled
@ConditionalOnClass(name = "org.springframework.boot.actuate.endpoint.annotation.Endpoint")
@ConditionalOnProperty(
prefix = "simpleflow.log",
name = "actuator-enabled",
havingValue = "true",
matchIfMissing = true
)
public class LogActuatorAutoConfiguration {
/**
* 日志端点
*/
@Bean
@ConditionalOnMissingBean
@ConditionalOnAvailableEndpoint
public LogEndpoint logEndpoint() {
return new LogEndpoint();
}
/**
* 日志健康指标
*/
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledHealthIndicator("log")
public LogHealthIndicator logHealthIndicator() {
return new LogHealthIndicator();
}
}
spring.factories配置
在src/main/resources/META-INF/spring.factories
中配置:
c
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.simpleflow.log.springboot.autoconfigure.LogAutoConfiguration,\
com.simpleflow.log.springboot.autoconfigure.LogWebAutoConfiguration,\
com.simpleflow.log.springboot.autoconfigure.LogActuatorAutoConfiguration
spring-configuration-metadata.json
在src/main/resources/META-INF/spring-configuration-metadata.json
中提供配置提示:
json
{
"groups": [
{
"name": "simpleflow.log",
"type": "com.simpleflow.log.springboot.properties.LogProperties",
"sourceType": "com.simpleflow.log.springboot.properties.LogProperties"
}
],
"properties": [
{
"name": "simpleflow.log.enabled",
"type": "java.lang.Boolean",
"description": "是否启用SimpleFlow日志框架",
"defaultValue": true
},
{
"name": "simpleflow.log.default-level",
"type": "com.simpleflow.log.annotation.LogLevel",
"description": "默认日志级别",
"defaultValue": "INFO"
},
{
"name": "simpleflow.log.web-enabled",
"type": "java.lang.Boolean",
"description": "是否启用Web集成",
"defaultValue": true
},
{
"name": "simpleflow.log.log-args",
"type": "java.lang.Boolean",
"description": "是否记录方法参数",
"defaultValue": true
},
{
"name": "simpleflow.log.log-result",
"type": "java.lang.Boolean",
"description": "是否记录方法返回值",
"defaultValue": true
},
{
"name": "simpleflow.log.global-sensitive-fields",
"type": "java.lang.String[]",
"description": "全局敏感字段配置",
"defaultValue": ["password", "pwd", "secret", "token", "key"]
}
]
}
使用示例
1. 添加依赖
xml
<dependency>
<groupId>com.simpleflow</groupId>
<artifactId>simpleflow-log-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
2. 配置文件
yaml
# application.yml
simpleflow:
log:
enabled: true
default-level: INFO
web-enabled: true
log-args: true
log-result: true
global-sensitive-fields:
- password
- idCard
- phone
request-log:
enabled: true
log-headers: false
log-parameters: true
exclude-patterns:
- /health
- /actuator/**
performance:
async-enabled: false
cache-size: 500
max-log-length: 10000
3. 业务代码
less
@RestController
@LogClass(prefix = "用户API")
public class UserController {
@GetMapping("/users/{id}")
@LogMethod
public User getUser(@PathVariable Long id) {
// 业务逻辑
return userService.findById(id);
}
}
4. 启动类
typescript
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
// 框架自动启用,无需额外配置
}
}
测试自动配置
typescript
@SpringBootTest
class LogAutoConfigurationTest {
@Autowired(required = false)
private LogMethodProcessor logMethodProcessor;
@Autowired(required = false)
private AnnotationConfigResolver configResolver;
@Test
void testAutoConfiguration() {
// 验证核心组件自动装配
assertNotNull(logMethodProcessor);
assertNotNull(configResolver);
}
@Test
@TestPropertySource(properties = "simpleflow.log.enabled=false")
void testDisabledConfiguration() {
// 当配置禁用时,组件不应该被装配
// 这个测试需要重新启动上下文
}
}
本章小结
✅ 完成的任务
- 配置属性:设计了完整的LogProperties配置类
- 条件装配:实现了ConditionalOnLogEnabled条件注解
- 自动配置:创建了多个AutoConfiguration类
- 元数据文件:提供了配置提示和自动完成
- 使用示例:展示了开箱即用的体验
🎯 学习要点
- Spring Boot自动配置的工作原理
- @Conditional条件装配的使用技巧
- ConfigurationProperties的设计模式
- spring.factories的作用机制
- 配置元数据的重要性
💡 思考题
- 如何设计向后兼容的配置属性?
- 自动配置的执行顺序如何控制?
- 如何为配置属性提供校验?
🚀 下章预告
下一章我们将集成Spring Boot Actuator,实现监控和运维功能,包括健康检查、指标暴露、运行时配置调整等企业级特性。
💡 设计原则 : 优秀的Starter应该做到零配置启用、灵活配置、智能条件装配。让用户专注业务逻辑,框架处理技术细节。