Spring Boot 钩子全集实战(五):ApplicationContextInitializer详解

Spring Boot 钩子全集实战(五):ApplicationContextInitializer详解

在上一篇中,我们深入剖析了SpringApplicationRunListener.environmentPrepared()这一关键扩展点,实现了环境合法性校验、启动上下文传递、多环境隔离兜底与配置动态增强等高阶能力。今天,我们将进入 Spring Boot 启动生命周期的下一个重要阶段——容器初始化前的关键扩展点ApplicationContextInitializer

一、什么是ApplicationContextInitializer

ApplicationContextInitializer是 Spring 框架原生提供的接口,在 Spring Boot 中被广泛用于ApplicationContext初始化完成前、刷新前(refresh 前)对容器进行定制化配置。其触发时机具有如下特征:

  • 触发时机ApplicationContext已创建但尚未调用refresh()
  • 核心状态Environment已完全准备就绪,所有配置已生效;
  • 执行顺序:晚于environmentPrepared(),早于BeanFactoryPostProcessor
  • 核心能力:可注册 Bean、修改 Bean 定义、设置容器属性、注入自定义逻辑。

核心价值:在容器刷新前对ApplicationContext做最后的“预热”和“定制”,是实现容器级扩展、安全加固、AOP 注入、监控埋点等场景的核心入口。

二、场景 :容器安全加固(禁止危险 Bean 注入)

业务痛点

微服务架构下,部分第三方库或内部组件可能通过自动配置注入高危 Bean(如RuntimeScriptEngineJndiTemplate),存在远程代码执行(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 ApplicationContextInitializer<ConfigurableApplicationContext> { // 高危类黑名单(可根据安全策略动态加载) private static final Set<String> 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.580+08: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.581+08:00 INFO 11158 --- [ main] com.example.demo.DemoApplication : The following 1 profile is active: "prod" [安全加固] 检测到高危 Bean:scriptEngine(类型:javax.script.ScriptEngine),已移除! [安全加固] 检测到高危 Bean:initialContext(类型:javax.naming.InitialContext),已移除! [安全加固] 检测到高危 Bean:jndiTemplate(类型:org.springframework.jndi.JndiTemplate),已移除! [安全加固] 检测到高危 Bean:runtime(类型:java.lang.Runtime),已移除! 2025-12-17T23:58:06.876+08:00 INFO 11158 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http) 2025-12-17T23:58:06.882+08:00 INFO 11158 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2025-12-17T23:58:06.883+08:00 INFO 11158 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.49] 2025-12-17T23:58:06.901+08:00 INFO 11158 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2025-12-17T23:58:06.901+08:00 INFO 11158 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 303 ms 2025-12-17T23:58:07.038+08: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.042+08: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 钩子源码” 获取完整源码!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1132003.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Git核心操作实战】从初始化到冲突解决与回滚(附完整演示)

文章目录目录引言环境准备一、仓库初始化与首次提交1.1 初始化 Git 仓库1.2 创建 .gitignore 文件1.3 首次提交技术解析二、分支创建与多轮小步提交2.1 切出 feature 分支2.2 2-3 次小步提交第一次提交&#xff1a;新增功能基础文件第二次提交&#xff1a;实现核心逻辑第三次提…

Teams Webhook 传递长文本的技巧与示例

引言 在团队协作工具中,Microsoft Teams和Slack是两个非常流行的选择。它们都支持通过Webhook发送消息,但有时你会发现Teams在处理长文本消息时会遇到一些限制。本文将详细介绍如何使用Teams Webhook传递多行文本,并确保URL链接可以点击,内容完整显示,就像用户直接输入一…

Apache Paimon多模态数据湖实践:从结构化到非结构化的技术演进

在近期的 Streaming Lakehouse Meetup Online EP.2&#xff5c;Paimon StarRocks 共话实时湖仓 直播中&#xff0c;Apache Paimon PMC 成员/阿里云数据湖资深工程师叶俊豪带来了关于 Paimon 多模态数据湖的深度技术分享。随着大模型训练对数据规模与多样性的要求不断提升&…

利用多进程提升图表模拟程序的性能

引言 在实时数据处理和图表模拟的领域,程序的响应速度和效率至关重要。特别是当我们处理大量数据并需要实时更新图表时,如何高效地利用系统资源就成为了一个关键问题。今天我们来探讨如何通过多进程来优化一个图表模拟程序的性能。 问题描述 我们有一款图表模拟程序,用于…

基于Java+SpringBoot+SSM儿童医院挂号管理系统(源码+LW+调试文档+讲解等)/儿童医院预约系统/儿童医院就诊管理系统/医院挂号管理系统/儿童医院挂号平台/儿童医院在线挂号

博主介绍 &#x1f497;博主介绍&#xff1a;✌全栈领域优质创作者&#xff0c;专注于Java、小程序、Python技术领域和计算机毕业项目实战✌&#x1f497; &#x1f447;&#x1f3fb; 精彩专栏 推荐订阅&#x1f447;&#x1f3fb; 2025-2026年最新1000个热门Java毕业设计选题…

网上租赁系统信息管理系统源码-SpringBoot后端+Vue前端+MySQL【可直接运行】

摘要 随着互联网技术的快速发展&#xff0c;传统租赁行业正逐步向数字化转型。网上租赁系统作为一种高效、便捷的商业模式&#xff0c;能够有效解决传统租赁过程中信息不对称、管理效率低下等问题。该系统通过整合线上资源&#xff0c;为用户提供租赁物品的浏览、下单、支付及管…

总结了 12 个嵌入式项目

前两天有一个读者问我&#xff0c;如果要做嵌入式项目&#xff0c;哪些项目会比较合适&#xff0c;这里总结了 12 个比较有代表性的项目&#xff0c;使用的cpu 也是主流的&#xff0c;推荐给大家&#xff0c;希望对大家学习有所帮助。1. Avem&#xff1a;轻量级无人机飞控项目项…

使用aop切面springmvc后抛出异常一直捕捉不到异常(抛出异常UndeclaredThrowableException类)

WebLogControllerAop这是一个切面处理类&#xff0c;使用的Around处理切面&#xff0c;有异常必须抛出&#xff0c;不然全局异常捕捉不到的 package cn.geg.lifecycle.config;import cn.geg.lifecycle.util.WebLogUtils; import cn.hutool.core.collection.CollUtil; import cn…

在Azure中实现跨订阅的AMPLS自动链接策略

在Azure环境中,管理和自动化资源链接是提升运维效率和确保安全性的关键。特别是在处理不同订阅的资源时,如何高效地实现跨订阅的自动链接是一个常见的挑战。本文将通过一个实际的案例,详细介绍如何利用Azure Policy实现Application Insights与AMPLS(Azure Monitor Private …

Java Web Web足球青训俱乐部管理后台系统系统源码-SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0【含文档】

摘要 足球青训俱乐部管理后台系统的开发旨在解决传统足球青训俱乐部在管理学员信息、课程安排、教练分配等方面效率低下的问题。随着足球运动的普及和青训体系的完善&#xff0c;俱乐部需要一套高效、智能化的管理系统来优化运营流程。传统的人工记录和Excel表格管理方式容易导…

Flask应用中的实例路径问题探讨

引言 在Flask应用程序开发中,实例路径(instance path)是一个经常被忽视但又非常关键的概念。特别是在部署和管理数据库时,如果不正确处理实例路径,可能会导致一系列难以诊断的问题。本文将通过一个真实的案例,详细探讨Flask实例路径的设置和其在生产环境中的影响。 实例…

上班是一场冷静的交易

将上班视作一场冷静的交易&#xff0c;这并非 cynicism&#xff0c;而是一种珍贵的清醒。它像一副坚固的甲胄&#xff0c;保护我们在职业的疆场上不被无谓的情绪流矢所伤&#xff0c;不被暧昧的期望绑架。我们付出标定好的时间、技能与专注&#xff0c;换取等值的报酬、经验与履…

【2025最新】基于SpringBoot+Vue的师生健康信息管理系统管理系统源码+MyBatis+MySQL

摘要 在当今信息化时代&#xff0c;校园健康管理已成为教育机构重点关注的方向之一。传统的师生健康信息管理多依赖纸质记录或分散的电子表格&#xff0c;存在数据冗余、更新滞后、查询效率低下等问题。随着互联网技术的快速发展&#xff0c;构建一个高效、安全、便捷的师生健康…

实时语音识别回声消除技巧

&#x1f493; 博客主页&#xff1a;借口的CSDN主页 ⏩ 文章专栏&#xff1a;《热点资讯》 实时语音识别回声消除的实战技巧&#xff1a;突破传统局限目录实时语音识别回声消除的实战技巧&#xff1a;突破传统局限 引言&#xff1a;回声消除——语音识别的隐形守护者 现在时&am…

《斯图尔特微积分(下册)》什么时候出?带答案吗???

《斯图尔特微积分》上册中文版9月出版后&#xff0c;读者评价“讲解语言生动&#xff0c;带入生活实际&#xff0c;能够了解数学公式或定理在其他学科的应用。”“良好的阅读体验&#xff0c;美观的版面和由浅入深的内容。”评论和后台常有人私信下册什么时候出&#xff1f;答案…

解决Unity中按钮点击索引问题

在使用Unity开发游戏或应用时,经常会遇到需要为多个按钮动态添加点击事件并传递索引参数的情况。然而,这种操作在C#中可能会导致一些意想不到的问题。本文将通过一个实际例子,解释这些问题及其解决方案。 问题描述 假设我们有一个ScrollView组件,其内容包含多个Button对象…

python开发中虚拟环境配置

在Python开发中&#xff0c;虚拟环境是项目隔离的最佳实践。以下是详细的使用指南&#xff1a; 1. 为什么需要虚拟环境&#xff1f; 依赖隔离&#xff1a;不同项目可以使用不同版本的包避免冲突&#xff1a;防止系统Python环境被污染便于部署&#xff1a;可以精确导出项目依赖团…

【毕业设计】SpringBoot+Vue+MySQL 社区医院信息平台平台源码+数据库+论文+部署文档

摘要 随着信息技术的快速发展&#xff0c;传统社区医院管理模式逐渐暴露出效率低下、信息孤岛等问题。社区医院作为基层医疗服务的重要载体&#xff0c;亟需通过信息化手段优化业务流程&#xff0c;提升服务质量。当前&#xff0c;社区医院在患者档案管理、药品库存监控、预约挂…

Day32 >> 56、合并区间 + 738、单调递增的数字

代码随想录-贪心算法Part5 56、合并区间 class Solution {public int[][] merge(int[][] intervals) {List<int[]> result new LinkedList<>();Arrays.sort(intervals, (x, y) -> Integer.compare(x[0], y[0]));int start intervals[0][0];int end interva…

驾驶认知的本质:人类模式 vs 端到端自动驾驶

在讨论自动驾驶系统时&#xff0c;一个常见的误解是把“开车能力”等同于“驾驶智能”。事实上&#xff0c;人类驾驶与端到端自动驾驶之间的核心差异&#xff0c;并不在于动作精度或感知能力&#xff0c;而在于认知结构与任务管理模式。一、人类驾驶&#xff1a;动态任务管理的…