SpringBoot32-ApplicationContext讲解

📅 2026/7/25 21:30:31
SpringBoot32-ApplicationContext讲解
一、没有容器之前我们是怎么创建对象的假设你在写一个电商系统有OrderService、UserService、ProductServicepublic class OrderService { private UserService userService new UserService(); private ProductService productService new ProductService(); public void createOrder() { userService.checkUser(); productService.checkStock(); // ... } }这种写法有什么问题问题 1对象之间的依赖是硬编码的OrderService内部直接new UserService()这意味着OrderService和UserService的具体实现类是强绑定的。如果你想换成UserServiceImpl2必须修改OrderService的源代码。问题 2对象的生命周期无法统一管理每个new出来的对象什么时候销毁、资源怎么释放散落在各个类的代码里没有统一的地方管。问题 3无法方便地切换实现比如测试环境和生产环境要用不同的实现你得改代码或者写一堆 if-else。二、第一个解决方案工厂模式为了解耦有人想到用工厂public class ServiceFactory { public static UserService getUserService() { return new UserService(); } }OrderService不再直接new而是通过工厂获取public class OrderService { private UserService userService ServiceFactory.getUserService(); }但工厂模式也有问题工厂类本身又变成了新的硬编码中心如果UserService又依赖了EmailService工厂里还得手动处理这层依赖对象的创建顺序、依赖关系全部要程序员在工厂里手动维护本质上工厂只是把在哪里 new换了个地方没有从根本上解决对象依赖的管理问题。三、Spring 的第一次进化BeanFactorySpring 框架的设计者意识到对象的创建和依赖关系应该由框架来统一管理而不是程序员手动维护。于是 Spring 创造了BeanFactoryBean 工厂// 你告诉 Spring这个类交给你管 Component public class UserService { } Component public class OrderService { Autowired private UserService userService; // Spring 会自动注入 }BeanFactory 做了什么读取配置知道哪些类需要被管理通过 XML、注解或配置类创建对象根据配置把对象创建出来这些被管理的对象就叫Bean解决依赖如果 A 依赖 B先创建 B再注入到 A 中缓存对象默认单例创建一次到处复用但 BeanFactory 还不够BeanFactory 是一个懒加载的容器——只有当代码真正去要某个 Bean 时它才会创建。这在实际开发中带来了一些问题问题 1启动时无法做统一初始化比如你的系统启动时需要加载配置文件到内存初始化数据库连接池启动定时任务BeanFactory 是按需创建它不会在启动时就帮你做这些事。问题 2缺少企业级功能实际项目中你需要国际化支持i18n事件发布与监听机制资源加载从 classpath、文件系统、URL 加载资源与 Spring AOP 的集成BeanFactory 太纯粹了它只负责 Bean 的创建和管理这些周边能力它没有。四、ApplicationContext 的诞生为什么需要它因为 BeanFactory 只解决了对象怎么创建和注入的问题但没有解决对象创建之后系统还需要什么配套能力的问题。所以 Spring 在 BeanFactory 的基础上扩展出了ApplicationContext应用上下文。你可以这样理解它们的关系ApplicationContext 继承自 BeanFactory ↓ ApplicationContext BeanFactory核心能力 各种企业级扩展五、ApplicationContext 到底多了什么1. 立即加载Eager InitializationApplicationContext 在启动时就会把所有单例 Bean 都创建好并放入容器。为什么这样设计提前暴露问题如果某个 Bean 的依赖有问题启动时就报错而不是等到用户请求时才报错启动时完成初始化连接池、缓存预热、定时任务注册都可以在启动时完成提升运行时性能用户请求来时Bean 已经准备好了直接拿来用2. 事件机制ApplicationEvent// 定义事件 public class OrderCreatedEvent extends ApplicationEvent { private Long orderId; public OrderCreatedEvent(Object source, Long orderId) { super(source); this.orderId orderId; } } // 发布事件 Autowired private ApplicationEventPublisher publisher; public void createOrder() { // ... 创建订单 publisher.publishEvent(new OrderCreatedEvent(this, orderId)); } // 监听事件 Component public class OrderEventListener { EventListener public void handleOrderCreated(OrderCreatedEvent event) { // 发送短信、更新统计等 } }为什么需要事件机制没有事件机制时A 做完了事要通知 BA 必须直接调用 B 的方法public class OrderService { Autowired private SmsService smsService; Autowired private StatisticsService statisticsService; public void createOrder() { // ... 创建订单 smsService.sendSms(); statisticsService.update(); } }弊端OrderService知道SmsService和StatisticsService的存在耦合度高。每增加一个订单创建后要做的事都要改OrderService的代码。有了事件机制后OrderService只负责发事件我创建好了你们谁爱处理谁处理其他服务通过监听事件来响应新增逻辑不需要改OrderService3. 资源加载ResourceLoaderApplicationContext 可以统一从各种来源加载资源// 从 classpath 加载 Resource resource context.getResource(classpath:application.yml); // 从文件系统加载 Resource resource context.getResource(file:/home/config/app.yml); // 从 URL 加载 Resource resource context.getResource(https://example.com/config.yml);为什么需要这个没有统一资源加载器时你的代码里会混杂new FileInputStream(...)读文件getClass().getResourceAsStream(...)读 classpathnew URL(...).openStream()读网络每种方式 API 不同代码混乱。ApplicationContext 提供统一抽象屏蔽了底层差异。4. 国际化支持MessageSourceAutowired private MessageSource messageSource; public String getMessage(String code) { return messageSource.getMessage(code, null, Locale.CHINA); }为什么需要这个没有 MessageSource 时错误提示、界面文案硬编码在代码里。要支持中英文切换你得自己写一套加载.properties文件的逻辑。ApplicationContext 内置了这个能力。5. 环境配置管理EnvironmentApplicationContext 持有Environment对象统一管理application.properties/application.yml中的配置系统环境变量JVM 启动参数Autowired private Environment env; public void someMethod() { String dbUrl env.getProperty(spring.datasource.url); }为什么需要 Environment没有统一配置管理时配置散落在各处代码里、XML 里、系统变量里。Environment 把它们整合到一个统一的抽象层并且支持按 profiledev/test/prod切换。六、在 Spring Boot 中ApplicationContext 是怎么工作的Spring Boot 的启动入口SpringBootApplication public class MyApp { public static void main(String[] args) { SpringApplication.run(MyApp.class, args); } }SpringApplication.run()内部做了什么事简化版流程1. 创建 SpringApplication 实例 2. 推断应用类型Servlet / Reactive / None 3. 加载 ApplicationContextInitializer上下文初始化器 4. 加载 ApplicationListener监听器 5. 【核心】创建 ApplicationContext 实例 6. 准备 Environment加载 application.yml 等配置 7. 刷新 ApplicationContext - 扫描 Component、Service、Controller 等注解 - 创建所有 Bean 并解决依赖 - 执行各种后置处理器 - 发布 ContextRefreshedEvent 事件 8. 启动内嵌的 Tomcat如果是 Web 应用 9. 发布 ApplicationStartedEvent 事件你可以直接拿到 ApplicationContextComponent public class SomeService implements ApplicationContextAware { private ApplicationContext context; Override public void setApplicationContext(ApplicationContext context) { this.context context; } public void doSomething() { // 从容器中获取 Bean UserService userService context.getBean(UserService.class); // 获取配置 String appName context.getEnvironment().getProperty(spring.application.name); // 发布事件 context.publishEvent(new SomeEvent(this)); } }但注意在 Spring Boot 中你通常不需要直接操作ApplicationContext因为依赖注入Autowired已经帮你做了。直接操作 ApplicationContext 是兜底方案比如你要写一些框架级别的代码时才会用到。七、总结ApplicationContext 的设计逻辑阶段没有它时的弊端解决方案对象创建手动 new硬编码依赖BeanFactory统一管理 Bean 的创建和注入启动初始化懒加载启动时无法预热ApplicationContext启动时立即初始化所有单例 Bean模块通信直接调用耦合度高ApplicationEvent发布-订阅模式解耦资源加载API 不统一代码混乱ResourceLoader统一加载 classpath/文件/URL 资源多语言文案硬编码MessageSource统一国际化配置管理配置散落在各处Environment统一整合各种配置来源ApplicationContext 的本质它是一个超级容器不仅管理 Bean 的生命周期还提供了应用运行所需的一切基础设施事件、配置、资源、国际化。它是 Spring 框架的运行环境你的所有 Bean 都活在它里面。所以可以这么理解ApplicationContext 就是 Spring IOC 容器的一种实现。ApplicationContext是Spring提供的高级IOC容器接口它继承了BeanFactory在Bean管理的基础上增加了事件发布、国际化、资源访问等企业级功能。在Spring Boot中启动项目时会创建ApplicationContext通过refresh方法完成容器初始化然后负责管理整个应用中的Bean生命周期。