第8篇:Spring Boot集成 - 开发自己的Starter

前言

前面我们实现了日志框架的核心功能,本章将创建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() {
        // 当配置禁用时,组件不应该被装配
        // 这个测试需要重新启动上下文
    }
}

本章小结

✅ 完成的任务

  1. 配置属性:设计了完整的LogProperties配置类
  2. 条件装配:实现了ConditionalOnLogEnabled条件注解
  3. 自动配置:创建了多个AutoConfiguration类
  4. 元数据文件:提供了配置提示和自动完成
  5. 使用示例:展示了开箱即用的体验

🎯 学习要点

  • Spring Boot自动配置的工作原理
  • @Conditional条件装配的使用技巧
  • ConfigurationProperties的设计模式
  • spring.factories的作用机制
  • 配置元数据的重要性

💡 思考题

  1. 如何设计向后兼容的配置属性?
  2. 自动配置的执行顺序如何控制?
  3. 如何为配置属性提供校验?

🚀 下章预告

下一章我们将集成Spring Boot Actuator,实现监控和运维功能,包括健康检查、指标暴露、运行时配置调整等企业级特性。


💡 设计原则 : 优秀的Starter应该做到零配置启用、灵活配置、智能条件装配。让用户专注业务逻辑,框架处理技术细节。

相关推荐
2401_8315017312 分钟前
Linux之Docker虚拟化技术(一)
java·linux·docker
TPBoreas21 分钟前
架构设计模式七大原则
java·开发语言
自由的疯32 分钟前
Java 实现TXT文件导入功能
java·后端·架构
开开心心就好32 分钟前
PDF转长图工具,一键多页转图片
java·服务器·前端·数据库·人工智能·pdf·推荐算法
现在没有牛仔了35 分钟前
SpringBoot实现操作日志记录完整指南
java·spring boot·后端
小蒜学长39 分钟前
基于django的梧桐山水智慧旅游平台设计与开发(代码+数据库+LW)
java·spring boot·后端·python·django·旅游
浮游本尊1 小时前
Java学习第16天 - 分布式事务与数据一致性
java
浮游本尊1 小时前
Java学习第15天 - 服务网关与API管理
java
熙客2 小时前
Java:LinkedList的使用
java·开发语言
blueblood2 小时前
🗄️ JFinal 项目在 IntelliJ IDEA 中的 Modules 配置指南
java·后端