2026/5/24 2:12:50
网站建设
项目流程
教育平台oss做视频网站,网站创建,朋友圈推广平台,网站推广的看法Spring Boot 钩子全集实战#xff08;五#xff09;#xff1a;ApplicationContextInitializer 详解
在上一篇中#xff0c;我们深入剖析了 SpringApplicationRunListener.environmentPrepared() 这一关键扩展点#xff0c;实现了环境合法性校验、启动上下文传递、多环境…Spring Boot 钩子全集实战五ApplicationContextInitializer详解在上一篇中我们深入剖析了SpringApplicationRunListener.environmentPrepared()这一关键扩展点实现了环境合法性校验、启动上下文传递、多环境隔离兜底与配置动态增强等高阶能力。今天我们将进入 Spring Boot 启动生命周期的下一个重要阶段——容器初始化前的关键扩展点ApplicationContextInitializer。一、什么是ApplicationContextInitializerApplicationContextInitializer是 Spring 框架原生提供的接口在 Spring Boot 中被广泛用于在ApplicationContext初始化完成前、刷新前refresh 前对容器进行定制化配置。其触发时机具有如下特征触发时机ApplicationContext已创建但尚未调用refresh()核心状态Environment已完全准备就绪所有配置已生效执行顺序晚于environmentPrepared()早于BeanFactoryPostProcessor核心能力可注册 Bean、修改 Bean 定义、设置容器属性、注入自定义逻辑。✅核心价值在容器刷新前对ApplicationContext做最后的“预热”和“定制”是实现容器级扩展、安全加固、AOP 注入、监控埋点等场景的核心入口。二、场景 容器安全加固禁止危险 Bean 注入业务痛点微服务架构下部分第三方库或内部组件可能通过自动配置注入高危 Bean如Runtime、ScriptEngine、JndiTemplate存在远程代码执行RCE风险。即使通过配置禁用仍可能因依赖传递或版本升级被意外激活。解决方案利用ApplicationContextInitializer在容器刷新前扫描并移除/替换高危 Bean 定义从源头阻断安全隐患。实现代码package com.example.demo.initializer; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import org.springframework.core.type.filter.AssignableTypeFilter; import org.springframework.jndi.JndiTemplate; import org.springframework.util.StringUtils; import javax.naming.InitialContext; import javax.script.ScriptEngine; import java.util.Arrays; import java.util.HashSet; import java.util.Set; /** * 容器安全加固初始化器 */ public class SecurityHardeningInitializer implements ApplicationContextInitializerConfigurableApplicationContext { // 高危类黑名单可根据安全策略动态加载 private static final SetString DANGEROUS_BEAN_TYPES new HashSet(Arrays.asList( javax.script.ScriptEngine, javax.naming.InitialContext, org.springframework.jndi.JndiTemplate, java.lang.Runtime )); Override public void initialize(ConfigurableApplicationContext applicationContext) { System.out.println([安全加固] 开始扫描并清理高危 Bean 定义); //构造高危类黑名单的类 if (applicationContext instanceof BeanDefinitionRegistry registry) { BeanDefinitionBuilder builder1 BeanDefinitionBuilder.genericBeanDefinition(ScriptEngine.class); BeanDefinitionBuilder builder2 BeanDefinitionBuilder.genericBeanDefinition(InitialContext.class); BeanDefinitionBuilder builder3 BeanDefinitionBuilder.genericBeanDefinition(JndiTemplate.class); BeanDefinitionBuilder builder4 BeanDefinitionBuilder.genericBeanDefinition(Runtime.class); // 注册BeanDefinition registry.registerBeanDefinition(scriptEngine, builder1.getBeanDefinition()); registry.registerBeanDefinition(initialContext, builder2.getBeanDefinition()); registry.registerBeanDefinition(jndiTemplate, builder3.getBeanDefinition()); registry.registerBeanDefinition(runtime, builder4.getBeanDefinition()); } DefaultListableBeanFactory beanFactory (DefaultListableBeanFactory) applicationContext.getBeanFactory(); String[] beanNames beanFactory.getBeanDefinitionNames(); for (String beanName : beanNames) { var beanDefinition beanFactory.getBeanDefinition(beanName); String beanClassName beanDefinition.getBeanClassName(); if (StringUtils.hasText(beanClassName) DANGEROUS_BEAN_TYPES.contains(beanClassName)) { System.err.printf([安全加固] 检测到高危 Bean%s类型%s已移除%n, beanName, beanClassName); beanFactory.removeBeanDefinition(beanName); } } // 禁止通过 Import 或 ComponentScan 自动注册高危类 disableDangerousScanning(applicationContext); System.out.println([安全加固] 容器安全加固完成); } /** * 禁用高危类的组件扫描防止后续自动注册 */ private void disableDangerousScanning(ConfigurableApplicationContext context) { ClassPathScanningCandidateComponentProvider scanner new ClassPathScanningCandidateComponentProvider(false); scanner.addIncludeFilter(new AssignableTypeFilter(Runtime.class)); // 实际生产中可结合 AspectJ 或字节码工具拦截此处仅作示意 System.out.println([安全加固] 已禁用高危类自动扫描需配合编译期检查); } }配置方式任选其一方式 1通过spring.factories# resources/META-INF/spring.factories org.springframework.context.ApplicationContextInitializer\ com.example.demo.initializer.SecurityHardeningInitializer方式 2通过主类显式注册SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication app new SpringApplication(DemoApplication.class); app.addInitializers(new SecurityHardeningInitializer()); app.run(args); } }输出结果. ____ _ __ _ _ /\\ / ____ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | _ | _| | _ \/ _ | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) |____| .__|_| |_|_| |_\__, | / / / / |_||___//_/_/_/ :: Spring Boot :: (v3.5.8) [安全加固] 开始扫描并清理高危 Bean 定义 [安全加固] 已禁用高危类自动扫描需配合编译期检查 [安全加固] 容器安全加固完成 2025-12-17T23:58:06.58008:00 INFO 11158 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication using Java 21.0.9 with PID 11158 (/Users/wangmingfei/Documents/个人/05 java天梯之路/01 源码/03 每日打卡系列/daily-check-in/springboot钩子/demo/target/classes started by wangmingfei in /Users/wangmingfei/Documents/个人/05 java天梯之路/01 源码/03 每日打卡系列/daily-check-in/springboot钩子/demo) 2025-12-17T23:58:06.58108:00 INFO 11158 --- [ main] com.example.demo.DemoApplication : The following 1 profile is active: prod [安全加固] 检测到高危 BeanscriptEngine类型javax.script.ScriptEngine已移除 [安全加固] 检测到高危 BeaninitialContext类型javax.naming.InitialContext已移除 [安全加固] 检测到高危 BeanjndiTemplate类型org.springframework.jndi.JndiTemplate已移除 [安全加固] 检测到高危 Beanruntime类型java.lang.Runtime已移除 2025-12-17T23:58:06.87608:00 INFO 11158 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http) 2025-12-17T23:58:06.88208:00 INFO 11158 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2025-12-17T23:58:06.88308:00 INFO 11158 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.49] 2025-12-17T23:58:06.90108:00 INFO 11158 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2025-12-17T23:58:06.90108:00 INFO 11158 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 303 ms 2025-12-17T23:58:07.03808:00 INFO 11158 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path / 2025-12-17T23:58:07.04208:00 INFO 11158 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 0.645 seconds (process running for 0.792)生产价值从容器层面拦截高危 Bean比运行时检测更早、更彻底防止因依赖升级或配置错误引入安全漏洞三、总结ApplicationContextInitializer是 Spring Boot 启动流程中连接环境准备与容器刷新的关键桥梁它与environmentPrepared()形成“环境 → 容器”的完整扩展链路是构建企业级高可靠、高安全、高可观测性应用不可或缺的一环。关注我每天 5 分钟带你从 Java 小白变身编程高手 点赞 关注 转发让更多小伙伴一起进步 私信 “SpringBoot 钩子源码” 获取完整源码