boot-boost 项目架构设计文档

一、项目概述

boot-boost 是一个面向 Spring 框架高级特性的演练与验证项目,旨在系统性覆盖循环依赖与三级缓存SpEL 表达式引擎SpringFactoriesLoader 演进父子容器隔离原型 Bean 生命周期陷阱外部化配置优先级 以及Spring Boot 启动流程深度定制 七大主题。项目通过设计可运行的 boot-boost-demo 模块,用实际代码验证这些知识点,并提供启动性能监控、规则引擎等可复用组件。

根据前三个实战项目的知识覆盖情况,本项目特别填补以下空白:

  • LightORM(ORM 框架设计):未涉及容器启动流程与父子容器
  • DynamicDS(多数据源动态路由):未涉及 SpEL 表达式计算与规则引擎
  • BeanEye(Bean 生命周期监控):已监控生命周期,但未深入三级缓存、原型 Bean 泄漏、启动流程定制

因此,boot-boost 不仅是一个学习验证项目,更可作为一个 Spring Boot 启动诊断与扩展工具包 投入实际使用。

二、整体架构

2.1 模块划分

flowchart LR subgraph boot-boost direction TB A[boot-boost-core] -->|基础能力| B[boot-boost-spring-boot-starter] B -->|自动配置| C[boot-boost-demo] end A1[spel 规则引擎] --> A A2[lifecycle 启动流程定制] --> A A3[context 父子容器] --> A A4[monitor 性能监控] --> A B1[autoconfigure 自动配置] --> B B2[initializer 初始化器] --> B B3[listener 启动监听器] --> B B4[resources/META-INF 注册文件] --> B C1[循环依赖 Service] --> C C2[SpEL 规则示例] --> C C3[父子容器配置] --> C C4[启动预热 Runner] --> C
  • boot-boost-core:纯逻辑组件,不依赖 Spring Boot,仅依赖 Spring Framework,提供 SpEL 规则引擎、启动流程扩展点接口、父子容器工具、监控核心逻辑。
  • boot-boost-spring-boot-starter :自动配置模块,负责将所有组件装配到 Spring Boot 应用中,同时提供 spring.factoriesAutoConfiguration.imports 两种注册方式,演示演进过程。
  • boot-boost-demo:可运行的演示应用,内含所有验证场景的代码,通过 profile 切换不同实验。

2.2 启动流程序列图

sequenceDiagram participant App as DemoApplication participant Runner as SpringApplication participant RL as SpringApplicationRunListener participant Env as EnvironmentPostProcessor participant Initializer as ApplicationContextInitializer participant Container as ApplicationContext participant BPP as BeanPostProcessor participant AppRunner as ApplicationRunner App->>Runner: run() Runner->>Runner: 创建 SpringApplication Runner->>RL: starting() Runner->>Env: postProcessEnvironment() Env-->>Runner: 添加自定义 PropertySource Runner->>RL: environmentPrepared() Runner->>Container: createApplicationContext() Runner->>Initializer: initialize() Initializer-->>Container: 注册额外 BeanDefinition Runner->>RL: contextPrepared() Runner->>Container: refresh() Container->>BPP: 实例化 Bean,触发三级缓存 Runner->>RL: contextLoaded() Runner->>RL: started() Runner->>AppRunner: run() AppRunner->>AppRunner: 预热缓存 Runner->>RL: running()

三、核心技术点设计与实现

3.1 循环依赖与三级缓存的验证

3.1.1 知识要点

  • Spring 通过三级缓存 解决单例 Bean 的设值循环依赖:
    • singletonObjects(一级):完全初始化好的 Bean
    • earlySingletonObjects(二级):提早曝光的 Bean 引用(可能未完成属性填充)
    • singletonFactories(三级):ObjectFactory,可生成代理对象的工厂
  • 构造器注入无法解决循环依赖,因为此时对象尚未实例化,无法提前暴露引用。
  • @Lazy 注解通过生成代理对象延迟注入,可破解构造器循环依赖,但会引入类型转换问题。
  • 原型作用域循环依赖无论何种注入方式均无法解决,因为原型 Bean 不进入缓存。

3.1.2 实现代码

ServiceA.java(构造器注入,无法启动)

java 复制代码
@Service
public class ServiceA {
    private final ServiceB serviceB;

    public ServiceA(ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}

ServiceB.java

java 复制代码
@Service
public class ServiceB {
    private final ServiceA serviceA;

    public ServiceB(ServiceA serviceA) {
        this.serviceA = serviceA;
    }
}

预期 :启动抛出 BeanCurrentlyInCreationException

修改为设值注入(启动成功)

java 复制代码
@Service
public class ServiceA {
    @Autowired
    private ServiceB serviceB;
}

@Service
public class ServiceB {
    @Autowired
    private ServiceA serviceA;
}

通过 BeanEye 风格监控三级缓存

自定义 BeanPostProcessor 结合反射获取 DefaultSingletonBeanRegistry 内部的缓存:

java 复制代码
@Component
public class CacheMonitorBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {
    private DefaultListableBeanFactory beanFactory;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.beanFactory = (DefaultListableBeanFactory) ((GenericApplicationContext) applicationContext).getBeanFactory();
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if ("serviceA".equals(beanName) || "serviceB".equals(beanName)) {
            logCacheState(beanName);
        }
        return bean;
    }

    private void logCacheState(String trigger) {
        try {
            Field singletonObjectsField = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
            Field earlySingletonObjectsField = DefaultSingletonBeanRegistry.class.getDeclaredField("earlySingletonObjects");
            Field singletonFactoriesField = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonFactories");
            singletonObjectsField.setAccessible(true);
            earlySingletonObjectsField.setAccessible(true);
            singletonFactoriesField.setAccessible(true);

            Map<String, Object> singletonObjects = (Map<String, Object>) singletonObjectsField.get(beanFactory);
            Map<String, Object> earlySingletonObjects = (Map<String, Object>) earlySingletonObjectsField.get(beanFactory);
            Map<String, ObjectFactory<?>> singletonFactories = (Map<String, ObjectFactory<?>>) singletonFactoriesField.get(beanFactory);

            System.out.printf("[%s] 三级缓存状态: 一级=%s, 二级=%s, 三级=%s%n",
                    trigger, singletonObjects.keySet(), earlySingletonObjects.keySet(), singletonFactories.keySet());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

使用 @Lazy 破解构造器循环依赖

java 复制代码
@Service
public class ServiceA {
    private final ServiceB serviceB;

    public ServiceA(@Lazy ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}

此时 serviceB 是一个代理对象,类型为 ServiceB$$EnhancerBySpringCGLIB。需要注意类型转换时使用接口或合理设计。

循环依赖解决流程图:

flowchart TB Start([创建 ServiceA]) --> A1{A的实例化} A1 --> A2[将A的ObjectFactory加入三级缓存] A2 --> A3[属性填充: 需要ServiceB] A3 --> B1[创建 ServiceB] B1 --> B2{实例化B} B2 --> B3[将B的ObjectFactory加入三级缓存] B3 --> B4[属性填充: 需要ServiceA] B4 --> C1{从缓存获取A} C1 --> C2[三级缓存有A的工厂] C2 --> C3[调用工厂获取早期引用, 移入二级缓存] C3 --> C4[注入A的早期引用到B] C4 --> B5[完成B的初始化, 移入一级缓存] B5 --> A4[注入完整的B到A] A4 --> A5[完成A的初始化, 移入一级缓存] A5 --> End([结束])

3.2 SpEL 表达式规则引擎

3.2.1 核心组件设计

@RuleExpression 注解

java 复制代码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RuleExpression {
    String value(); // SpEL 表达式
}

SpelRuleEngine 接口与实现

java 复制代码
public interface SpelRuleEngine {
    <T> T evaluate(String expression, Object rootObject, Map<String, Object> variables, Class<T> returnType);
    boolean evaluateAsBoolean(String expression, Map<String, Object> variables);
    void addCachedExpression(String key, String expression);
}

@Component
public class DefaultSpelRuleEngine implements SpelRuleEngine {
    private final SpelExpressionParser parser = new SpelExpressionParser();
    private final Map<String, Expression> expressionCache = new ConcurrentHashMap<>();

    private final EvaluationContext customContext; // 可通过配置选择 Simple 或 Standard

    public DefaultSpelRuleEngine(@Value("${boot-boost.spel.evaluation-context:simple}") String contextType) {
        if ("simple".equals(contextType)) {
            this.customContext = SimpleEvaluationContext.forReadOnlyDataBinding().build();
        } else {
            this.customContext = new StandardEvaluationContext();
        }
    }

    @Override
    public <T> T evaluate(String expressionStr, Object rootObject, Map<String, Object> variables, Class<T> returnType) {
        Expression expression = expressionCache.computeIfAbsent(expressionStr, parser::parseExpression);
        EvaluationContext context = createContext(rootObject, variables);
        return expression.getValue(context, returnType);
    }

    private EvaluationContext createContext(Object rootObject, Map<String, Object> variables) {
        EvaluationContext context = (this.customContext instanceof StandardEvaluationContext)
                ? new StandardEvaluationContext(rootObject) : SimpleEvaluationContext.forReadOnlyDataBinding().withRootObject(rootObject).build();
        if (variables != null) {
            variables.forEach(context::setVariable);
        }
        return context;
    }
}

安全防护对比

  • StandardEvaluationContext:允许类型引用、方法调用、构造器调用,存在注入风险(如 T(java.lang.Runtime).exec('calc'))。
  • SimpleEvaluationContext:仅允许属性访问和只读操作,禁止类型引用,从根源杜绝代码注入。

@ConditionalOnExpression 简化版实现

java 复制代码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(OnSpelExpressionCondition.class)
public @interface ConditionalOnSpEL {
    String value();
}

public class OnSpelExpressionCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Map<String, Object> attrs = metadata.getAnnotationAttributes(ConditionalOnSpEL.class.getName());
        String expression = (String) attrs.get("value");
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        StandardEvaluationContext evalContext = new StandardEvaluationContext();
        evalContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
        ExpressionParser parser = new SpelExpressionParser();
        return Boolean.TRUE.equals(parser.parseExpression(expression).getValue(evalContext, Boolean.class));
    }
}

SpEL 表达式解析流程

flowchart TD A[规则字符串] --> B{缓存命中?} B -- 是 --> C[获取已解析Expression] B -- 否 --> D[SpelExpressionParser解析] D --> E[生成Expression对象] E --> C C --> F[创建EvaluationContext] F --> G[注册BeanResolver和变量] G --> H[执行getValue] H --> I[返回结果]

3.3 SpringFactoriesLoader 与 AutoConfiguration.imports 的演进对比

3.3.1 传统方式与新版方式

传统方式(spring.factories)

properties 复制代码
# META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.bootboost.autoconfigure.BootBoostAutoConfiguration
  • 加载类:SpringFactoriesLoader
  • 文件路径:META-INF/spring.factories
  • 始于 Spring 3.2,多种扩展点共用一个文件,语义不清晰,且每次加载该文件都会实例化所有工厂类,存在性能开销。

新版方式(AutoConfiguration.imports)

shell 复制代码
# META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.bootboost.autoconfigure.BootBoostAutoConfiguration
  • ImportCandidates.load() 加载,Spring Boot 2.7 引入
  • 文件专用于自动配置,语义清晰,配合新的 @AutoConfiguration 注解
  • 加载性能更好,按需加载,支持条件过滤

BootBoostAutoConfiguration 示例

java 复制代码
@AutoConfiguration
@ConditionalOnSpEL("${boot-boost.enabled:true}")
@EnableConfigurationProperties(BootBoostProperties.class)
public class BootBoostAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public SpelRuleEngine spelRuleEngine(BootBoostProperties properties) {
        return new DefaultSpelRuleEngine(properties.getSpel().getEvaluationContext());
    }

    @Bean
    public StartupApplicationListener startupApplicationListener() {
        return new StartupApplicationListener();
    }
}

对比实验设计 : 在启动类同时使用两种注册方式,通过 BeanPostProcessorApplicationListener 记录加载顺序日志,验证新版导入先于传统 spring.factories 被解析。

特性 spring.factories AutoConfiguration.imports
文件位置 META-INF/spring.factories META-INF/spring/...imports
加载时机 Spring Boot 构建启动时完整扫描 自动配置专用阶段
语义清晰度 混合多个扩展点,不易维护 专用于自动配置
性能 全量加载,可能实例化无用类 按需加载,结合条件过滤
引入版本 Spring 3.2+ / Boot 1.0+ Boot 2.7+

3.4 父子容器与模块隔离

设计父子容器结构模拟 Spring MVC 的 Root WebApplicationContextServlet WebApplicationContext 模型。

父容器配置(基础设施)

java 复制代码
@Configuration
@ComponentScan(basePackages = "com.example.demo.shared")
public class ParentContainerConfig {
    @Bean
    public DataSource dataSource() {
        // H2 内存数据库
        return new EmbeddedDatabaseBuilder()
                .setType(EmbeddedDatabaseType.H2)
                .build();
    }

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

子容器配置(业务模块)

java 复制代码
@Configuration
@ComponentScan(basePackages = "com.example.demo.module")
public class ChildContainerConfig {
    // 模块特有的Bean
    @Autowired
    private DataSource dataSource; // 从父容器获取
}

演示代码

java 复制代码
public class ParentChildContainerTest {
    public static void main(String[] args) {
        // 创建父容器
        AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext(ParentContainerConfig.class);
        
        // 创建子容器并设置父容器
        AnnotationConfigApplicationContext child = new AnnotationConfigApplicationContext();
        child.setParent(parent);
        child.register(ChildContainerConfig.class);
        child.refresh();

        // 子容器可访问父容器的Bean
        DataSource ds = child.getBean(DataSource.class);
        System.out.println("从子容器获取DataSource成功: " + ds);

        // 父容器无法访问子容器的Bean
        try {
            parent.getBean(UserService.class);
        } catch (NoSuchBeanDefinitionException e) {
            System.out.println("父容器无法获取UserService: " + e.getMessage());
        }
    }
}

交互图

flowchart BT subgraph 子容器 Controller UserService end subgraph 父容器 DataSource TransactionManager end Controller -->|调用| UserService UserService -->|注入| DataSource TransactionManager -->|注入| DataSource

3.5 原型 Bean 循环依赖与资源泄漏

3.5.1 原型循环依赖验证

java 复制代码
@Component
@Scope("prototype")
public class PrototypeA {
    @Autowired
    private PrototypeB prototypeB;
}

@Component
@Scope("prototype")
public class PrototypeB {
    @Autowired
    private PrototypeA prototypeA;
}

// 每次调用 context.getBean(PrototypeA.class) 都会抛出 BeanCurrentlyInCreationException

原因:原型 Bean 不进入三级缓存,每次获取都重新创建,相互依赖导致无限循环。

3.5.2 资源泄漏模拟与处理

原型 Bean 持有资源:

java 复制代码
@Component
@Scope("prototype")
public class LeakyResourceBean implements DisposableBean {
    private final InputStream stream;

    public LeakyResourceBean() throws FileNotFoundException {
        this.stream = new FileInputStream("example.txt");
    }

    @Override
    public void destroy() throws Exception {
        stream.close(); // Spring不会自动调用原型Bean的destroy
        System.out.println("资源已释放");
    }
}

自定义 DestructionAwareBeanPostProcessor 手动管理

java 复制代码
@Component
public class PrototypeDestructionPostProcessor extends DestructionAwareBeanPostProcessorAdapter {
    private final Map<String, List<DisposableBean>> disposableBeans = new ConcurrentHashMap<>();

    @Override
    public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
        // 仅对原型Bean处理
    }

    @Override
    public boolean requiresDestruction(Object bean) {
        return bean instanceof DisposableBean;
    }

    // 在合适的时机调用 destroy,例如通过 @PreDestroy 自定义作用域或手动注册
}

更佳实践:使用 CustomScopeConfigurer 注册一个可管理销毁的自定义作用域,或由调用方显式调用 destroy

3.6 @Value 与 @ConfigurationProperties 的优先级对比

AppProperties

java 复制代码
@ConfigurationProperties(prefix = "app")
@Validated
public class AppProperties {
    @NotNull
    private String name;
    private String version;
    @Min(1)
    private int maxRetry;
    // getters/setters
}

对比验证类

java 复制代码
@RestController
@RequestMapping("/config")
public class ConfigComparisonController {
    @Value("${app.name}")
    private String nameFromValue;

    @Autowired
    private AppProperties appProperties;

    @GetMapping("/compare")
    public Map<String, Object> compare() {
        Map<String, Object> result = new HashMap<>();
        result.put("valueAnnotation", nameFromValue);
        result.put("configurationProperties", appProperties.getName());
        result.put("maxRetry", appProperties.getMaxRetry());
        return result;
    }
}

验证场景

  • 通过 application.yml 设置 app.name,两者一致。
  • 通过命令行参数 --app.name=cmd-value 覆盖,两者均更新(这是同一套 Environment,最终值一致)。
  • 松散绑定:@Value 需要使用精确的 app.name,而 @ConfigurationProperties 支持 appNameapp-nameAPP_NAME 等。可通过环境变量测试差异。
  • 校验:@ConfigurationProperties + @Validated 可校验属性值,@Value 不支持 JSR-303 校验。
  • 默认值:@Value("${app.timeout:5000}") 提供默认值,@ConfigurationProperties 则依靠字段默认值,属性缺失时保留字段初始值。

优先级链(外部化配置):

  1. 命令行参数
  2. JNDI 属性
  3. System.getProperties()
  4. 操作系统环境变量
  5. application-profile.yml
  6. application.yml
  7. @PropertySource
  8. SpringApplication.setDefaultProperties

3.7 Spring Boot 启动流程深度定制

3.7.1 ApplicationContextInitializer

java 复制代码
public class CustomApplicationContextInitializer implements ApplicationContextInitializer<GenericApplicationContext> {
    @Override
    public void initialize(GenericApplicationContext applicationContext) {
        // 注册额外Bean,比如程序化添加数据源代理
        applicationContext.registerBean("customizedBean", CustomizedBean.class);
        System.out.println("[Initializer] 容器初始化完成,已注册 customizedBean");
    }
}

注册在 META-INF/spring.factories

properties 复制代码
org.springframework.context.ApplicationContextInitializer=\
com.bootboost.initializer.CustomApplicationContextInitializer

3.7.2 SpringApplicationRunListener

java 复制代码
public class StartupApplicationListener implements SpringApplicationRunListener {

    private final SpringApplication application;
    private final String[] args;
    private long startTime;

    // 必须有此构造器
    public StartupApplicationListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
    }

    @Override
    public void starting(ConfigurableBootstrapContext bootstrapContext) {
        startTime = System.currentTimeMillis();
        System.out.println("=== 启动开始 ===");
    }

    @Override
    public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
        long cost = System.currentTimeMillis() - startTime;
        System.out.println("环境准备完成,耗时: " + cost + "ms");
    }

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        long cost = System.currentTimeMillis() - startTime;
        System.out.println("上下文准备完成,耗时: " + cost + "ms");
    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        long cost = System.currentTimeMillis() - startTime;
        System.out.println("上下文加载完成(Bean定义加载完成),耗时: " + cost + "ms");
    }

    @Override
    public void started(ConfigurableApplicationContext context, Duration timeTaken) {
        System.out.println("应用已启动,总耗时: " + timeTaken.toMillis() + "ms");
    }

    @Override
    public void running(ConfigurableApplicationContext context) {
        System.out.println("应用运行中...");
    }

    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {
        System.out.println("启动失败: " + exception.getMessage());
    }
}

注册同样在 spring.factories

3.7.3 EnvironmentPostProcessor

java 复制代码
public class CustomEnvironmentPostProcessor implements EnvironmentPostProcessor {
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        Map<String, Object> remoteProperties = Map.of("remote.config.name", "from-remote-center");
        environment.getPropertySources().addFirst(new MapPropertySource("remoteConfig", remoteProperties));
        System.out.println("[EnvironmentPostProcessor] 已添加远程配置源");
    }
}

3.7.4 FailureAnalyzer

java 复制代码
public class PortInUseFailureAnalyzer extends AbstractFailureAnalyzer<PortInUseException> {
    @Override
    protected FailureAnalysis analyze(Throwable rootFailure, PortInUseException cause) {
        return new FailureAnalysis(
                "端口 " + cause.getPort() + " 已被占用",
                "请检查端口占用情况或修改 server.port 配置",
                cause
        );
    }
}

3.8 启动性能监控与诊断

StartupMonitor :利用 StartupApplicationListener 收集阶段耗时并暴露 Actuator 端点。

java 复制代码
@Endpoint(id = "startup")
@Component
public class StartupMetricsEndpoint {
    private final StartupApplicationListener listener;

    @Autowired
    public StartupMetricsEndpoint(StartupApplicationListener listener) {
        this.listener = listener;
    }

    @ReadOperation
    public Map<String, Object> startupReport() {
        Map<String, Object> report = new LinkedHashMap<>();
        report.put("stages", listener.getStageDurations()); // 各阶段耗时
        report.put("slowBeans", getSlowBeans());
        return report;
    }

    private List<String> getSlowBeans() {
        // 从 BeanPostProcessor 收集的初始化耗时数据中筛选超过阈值的Bean
        return SlowBeanDetector.getSlowBeans();
    }
}

慢 Bean 检测

java 复制代码
@Component
public class SlowBeanDetector implements BeanPostProcessor {
    private static final Map<String, Long> beanInitTimes = new ConcurrentHashMap<>();
    private static long threshold = 500; // ms

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // 在 Before 记录时间,After 计算耗时,此处简化逻辑
        return bean;
    }
}

启动预热

java 复制代码
@Component
public class CacheWarmUpRunner implements ApplicationRunner {
    @Autowired
    private SpelRuleEngine ruleEngine;

    @Override
    public void run(ApplicationArguments args) {
        ruleEngine.addCachedExpression("vipCheck", "#{#user.isVip(#userId)}");
        // 预编译热点表达式
        System.out.println("SpEL 表达式预编译完成,启动预热结束");
    }
}

四、boot-boost-demo 完整测试项目

4.1 项目结构

css 复制代码
boot-boost-demo
├── src/main/java/com/example/demo
│   ├── DemoApplication.java
│   ├── service
│   │   ├── ServiceA.java
│   │   ├── ServiceB.java
│   │   ├── PrototypeA.java
│   │   └── PrototypeB.java
│   ├── spel
│   │   ├── RuleExpressionEvaluator.java
│   │   ├── SpelRuleEngine.java
│   │   └── VipDiscountRule.java
│   ├── config
│   │   ├── AppProperties.java
│   │   ├── ParentContainerConfig.java
│   │   └── ChildContainerConfig.java
│   ├── initializer
│   │   └── CustomApplicationContextInitializer.java
│   ├── listener
│   │   └── CustomSpringApplicationRunListener.java
│   └── runner
│       └── CacheWarmUpRunner.java
├── src/main/resources
│   ├── application.yml
│   └── META-INF
│       ├── spring.factories
│       └── spring
│           └── org.springframework.boot.autoconfigure.AutoConfiguration.imports
└── src/test/java/com/example/demo
    ├── CircularDependencyTest.java
    ├── SpelRuleEngineTest.java
    ├── ParentChildContainerTest.java
    └── PrototypeLeakTest.java

4.2 application.yml 配置示例

yaml 复制代码
boot-boost:
  enabled: true
  spel:
    cache-expressions: true
    evaluation-context: simple
  startup:
    warm-up-enabled: true
    slow-bean-threshold: 500ms
app:
  name: boot-boost-demo
  version: 1.0.0
  max-retry: 3

4.3 测试验证场景与预期

测试类 验证点 预期结果
CircularDependencyTest 构造器注入循环依赖 启动失败,抛出 BeanCurrentlyInCreationException
CircularDependencyTest 设值注入循环依赖 启动成功,通过监控端点查看三级缓存变动
SpelRuleEngineTest VIP 用户规则执行 #{@userService.isVip(#userId)} 返回 true
SpelRuleEngineTest SimpleEvaluationContext 安全性 执行 T(java.lang.Runtime).exec('calc') 抛出异常
ParentChildContainerTest 父子容器隔离 子容器可获取父 Bean,父容器无法获取子 Bean
PrototypeLeakTest 原型循环依赖 多次获取抛出异常,资源未自动释放
ConfigComparisonController 属性优先级与松散绑定 @Value 不支持松散绑定,@ConfigurationProperties 支持校验

五、与 Spring 核心系列文章知识关联表

文章编号/主题 boot-boost 中对应实现 知识点链接
第1篇 IoC容器 父子容器结构 HierarchicalBeanFactory、容器隔离
第2篇 Bean生命周期 SlowBeanDetector、原型Bean销毁 BeanPostProcessor、DisposableBean
第3篇 依赖注入 ServiceA/ServiceB 设值注入 @Autowired、@Lazy
第4篇 AOP @Lazy 生成代理对象对类型影响 代理与类型转换
第5篇 循环依赖终极剖析 三级缓存监控、构造器/设值对比 三级缓存流转、ObjectFactory
第6篇 @Import机制 @AutoConfiguration 新注解 ImportSelector 与自动配置
第7篇 SpEL及其在框架中的妙用 RuleExpressionEvaluator、SpelRuleEngine SpelExpressionParser、EvaluationContext
第8篇 容器扩展点 ApplicationContextInitializer、BeanFactoryPostProcessor 使用 扩展点回调顺序
第9篇 SpringFactoriesLoader到AutoConfiguration.imports 双注册方式对比实验 文件加载机制演进
第10篇 类型转换 @Lazy 代理对象对转换影响 ConversionService
第11篇 Spring MVC 启动全景:父子容器 父子容器设计与实验 父子容器交互
第12篇 设计模式 规则引擎的模板模式 策略模式、模板方法
第13篇 反模式与排查宝典 原型泄漏、循环依赖排查 BeanCurrentlyInCreationException 分析
第14篇 Spring Boot 启动流程 CustomSpringApplicationRunListener、启动预热 启动阶段分解
第15篇 自动配置原理 BootBoostAutoConfiguration、条件装配 @Conditional、AutoConfiguration
第16篇 MyBatis整合原理 (本文不涉及,前三项目已覆盖)

六、总结与展望

boot-boost 项目通过七大主题的深度演练,填补了 LightORM、DynamicDS、BeanEye 三个项目未覆盖的 Spring 核心高级特性。该项目不仅可用于学习和面试准备,其性能监控、规则引擎、启动诊断等组件更具备生产级落地的可能性。

本项目的后续演进方向包括:

  1. 将 SpEL 规则引擎扩展为支持数据库持久化与热更新。
  2. 将启动性能监控数据接入 Micrometer 与 Prometheus。
  3. 开发 IDEA 插件,可视化展示三级缓存变化和启动耗时火焰图。
  4. 与 BeanEye 合并,形成完整的 Spring 生命周期可观测性套件。 。
相关推荐
空中海2 小时前
03 MyBatis Spring Boot 集成、事务、测试与工程化体系
spring boot·后端·mybatis
杨运交3 小时前
[013][缓存模块]基于Redis的计数器缓存模板设计——AbstractCounterCacheTemplate 技术解析
spring boot·后端
ffqws_3 小时前
Spring @Transactional 注解详解:从入门到避坑
java·数据库·后端·spring
RuoyiOffice4 小时前
SpringBoot+Vue3 企业假期余额系统设计:账户、流水、预占、销假退回与到期清零全链路拆解
spring boot·后端·spring·vue·hr·企业管理软件·ruoyioffice
张小洛4 小时前
Spring 常用类深度剖析(工具篇 05):Assert:用断言代替 if-throw,代码更清爽
spring·log4j·参数校验·validate·assert·spring 常用类·代码简化
晚风_END14 小时前
Linux|操作系统|最新版openzfs编译记录
linux·运维·服务器·数据库·spring·中间件·个人开发
FYKJ_201014 小时前
springboot校园兼职平台--附源码02041
java·javascript·spring boot·python·eclipse·django·php
hERS EOUS16 小时前
SpringBoot 使用 spring.profiles.active 来区分不同环境配置
spring boot·后端·spring
超梦dasgg17 小时前
智慧充电系统设备管理服务对外接口实现方案
java·spring·微服务