引言
Spring框架的核心是控制反转(IoC) 和依赖注入(DI) ,而承载这一核心的正是ApplicationContext
接口及其实现类。无论是传统的XML配置时代,还是现代的Java注解与自动配置时代,ApplicationContext
始终是Spring应用的"神经中枢",负责Bean的生命周期管理、资源加载、事件传播等关键功能。
本文将通过5大实战场景 (手动BeanFactory加载、类路径/文件系统XML配置、Java注解配置、Web环境集成),结合源码级解析 和调试日志验证,从底层原理到实战应用,掌握Spring ApplicationContext的核心机制。
一、手动通过BeanFactory加载XML配置:IoC容器的"最小可用单元"
1.1 底层核心类解析
要理解Spring IoC的底层原理,必须从DefaultListableBeanFactory
和XmlBeanDefinitionReader
入手:
1.1.1 DefaultListableBeanFactory:IoC容器的"心脏"
DefaultListableBeanFactory
是Spring IoC容器的基础实现类,内部维护了两大核心数据结构:
- beanDefinitionMap :
ConcurrentHashMap<String, BeanDefinition>
,存储所有Bean的元数据(类、构造参数、属性、作用域等)。 - singletonObjects :
ConcurrentHashMap<String, Object>
,单例Bean的缓存(Spring默认作用域为单例)。
其他关键属性包括:
beanDefinitionNames
:所有Bean定义的名称列表(用于遍历)。autowireCapableBeanFactory
:支持自动装配的Bean工厂接口(处理依赖注入)。
1.1.2 XmlBeanDefinitionReader:XML配置的"翻译官"
XmlBeanDefinitionReader
负责将XML文件中的<bean>
标签转换为BeanDefinition
对象。其核心方法是loadBeanDefinitions(Resource resource)
,内部调用parseBeanDefinitions
解析XML,并通过registerBeanDefinition
将BeanDefinition
注册到DefaultListableBeanFactory
的beanDefinitionMap
中。
1.2 手动加载XML的完整流程
1.2.1 代码逐行分析
java
/**
* 手动通过 DefaultListableBeanFactory 加载 XML 配置(演示 BeanFactory 底层原理)
* <p>
* 底层原理:Spring IoC 容器的核心是 DefaultListableBeanFactory,它通过 BeanDefinitionReader 解析配置,
* 将 XML 中的 <bean> 标签转换为 BeanDefinition 对象(Bean 的"元数据蓝图"),并注册到容器中。
*/
private static void manualLoadXmlWithBeanFactory() {
log.info("===== 手动加载 XML 配置(BeanFactory 底层原理) =====");
// 步骤1:初始化 DefaultListableBeanFactory(IoC容器核心)
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
log.info("初始化后 BeanDefinition 数量:{}", beanFactory.getBeanDefinitionCount()); // 输出0
// 步骤2:创建 XmlBeanDefinitionReader(XML配置解析器)
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
log.debug("XmlBeanDefinitionReader 初始化完成。");
// 步骤3:加载类路径XML配置(触发解析与注册)
Resource classPathResource = new ClassPathResource("classPathXml.xml");
try {
int count = reader.loadBeanDefinitions(classPathResource); // 核心方法
log.info("加载 classPathXml.xml 后 BeanDefinition 数量:{}(新增{}个)",
beanFactory.getBeanDefinitionCount(), count); // 输出2(假设XML有2个Bean)
} catch (BeanDefinitionStoreException e) {
log.error("加载类路径XML失败", e);
}
// 步骤4:加载文件系统XML配置(跨位置配置加载)
Resource fileSystemResource = new FileSystemResource("spring_case/src/main/resources/fileSystemXml.xml"); // 实际开发建议使用相对路径
try {
int count = reader.loadBeanDefinitions(fileSystemResource);
log.info("加载 fileSystemXml.xml 后 BeanDefinition 数量:{}(新增{}个)",
beanFactory.getBeanDefinitionCount(), count); // 输出4(累计新增2个)
} catch (BeanDefinitionStoreException e) {
log.error("加载文件系统XML失败", e);
}
}
1.2.2 关键步骤源码追踪(以loadBeanDefinitions
为例)
XmlBeanDefinitionReader.loadBeanDefinitions(Resource resource)
的核心逻辑如下:
-
获取XML输入流 :通过
Resource.getInputStream()
读取XML文件内容。 -
解析XML文档 :使用
DocumentBuilderFactory
解析XML,得到Document
对象。 -
遍历
<bean>
标签 :对每个<bean>
标签,解析id
、class
、scope
、property
等属性。 -
生成BeanDefinition:
BeanDefinitionBuilder
根据标签属性构建GenericBeanDefinition
(最常见的BeanDefinition
实现类)。- 设置Bean的类(
setBeanClass(Class<?> beanClass)
)、作用域(setScope(String scope)
)、构造参数(setConstructorArgumentValues
)、属性值(setPropertyValues
)等。
-
注册BeanDefinition :调用
registerBeanDefinition(String beanName, BeanDefinition definition)
,将BeanDefinition
存入beanFactory.beanDefinitionMap
。
1.2.3 XML配置示例与验证
假设classPathXml.xml
内容如下:
xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="classPathXmlBeanA" class="com.dwl.bean_factory_case.ApplicationContextCase$ClassPathXmlBeanA">
<description>类路径XML配置的BeanA(无依赖)</description>
</bean>
<bean id="classPathXmlBeanB" class="com.dwl.bean_factory_case.ApplicationContextCase$ClassPathXmlBeanB">
<property name="classPathXmlBeanA" ref="classPathXmlBeanA"/>
<description>类路径XML配置的BeanB(依赖BeanA)</description>
</bean>
</beans>
通过调试日志可以看到,加载后beanFactory.beanDefinitionMap
中包含两个GenericBeanDefinition
对象,其中classPathXmlBeanB
的propertyValues
属性包含对classPathXmlBeanA
的引用。
二、类路径与文件系统XML配置的ApplicationContext
2.1 ClassPathXmlApplicationContext:类路径XML的"开箱即用"
ClassPathXmlApplicationContext
是ApplicationContext
的实现类,其构造方法默认调用refresh()
,完成容器初始化的全流程。
2.1.1 核心流程解析(基于refresh()源码)
refresh()
方法是Spring IoC容器的"启动引擎",包含以下关键步骤(简化版):
java
public abstract class AbstractApplicationContext implements ApplicationContext {
public final void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 1. 准备刷新上下文(初始化环境变量、设置启动时间等)
prepareRefresh();
// 2. 初始化BeanFactory(创建DefaultListableBeanFactory并配置)
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3. 配置BeanFactory(设置类加载器、表达式解析器、事件监听器等)
prepareBeanFactory(beanFactory);
try {
// 4. 后置处理BeanFactory(允许子类修改BeanFactory配置)
postProcessBeanFactory(beanFactory);
// 5. 实例化并调用BeanFactoryPostProcessor(处理Bean定义的后置逻辑)
invokeBeanFactoryPostProcessors(beanFactory);
// 6. 注册BeanPostProcessor(处理Bean实例的后置逻辑,如依赖注入)
registerBeanPostProcessors(beanFactory);
// 7. 初始化消息源(国际化支持)
initMessageSource();
// 8. 初始化应用事件多播器(事件传播)
initApplicationEventMulticaster();
// 9. 子类初始化其他特殊Bean(如Web环境中的DispatcherServlet)
onRefresh();
// 10. 注册事件监听器Bean到事件多播器
registerListeners();
// 11. 实例化所有非延迟加载的单例Bean(核心步骤!)
finishBeanFactoryInitialization(beanFactory);
// 12. 完成刷新(发布ContextRefreshedEvent事件)
finishRefresh();
} catch (BeansException ex) {
destroyBeans();
cancelRefresh(ex);
throw ex;
}
}
}
}
2.1.2 关键步骤详解
-
步骤2(obtainFreshBeanFactory) :创建
DefaultListableBeanFactory
,并设置其类加载器(默认使用上下文的类加载器)。 -
步骤5(invokeBeanFactoryPostProcessors) :执行
BeanFactoryPostProcessor
(如PropertySourcesPlaceholderConfigurer
,用于解析@Value
中的占位符)。 -
步骤11(finishBeanFactoryInitialization) :调用
DefaultListableBeanFactory.preInstantiateSingletons()
,实例化所有非延迟加载的单例Bean。此方法会遍历beanDefinitionNames
,对每个Bean执行:- 获取Bean定义(
getBeanDefinition
)。 - 确定Bean的作用域(单例/原型/请求等)。
- 创建Bean实例(通过反射调用无参构造器,或工厂方法)。
- 依赖注入(处理
@Autowired
、@Resource
注解,或XML中的<property>
标签)。 - 初始化Bean(调用
InitializingBean.afterPropertiesSet()
或init-method
方法)。
- 获取Bean定义(
2.1.3 验证Bean生命周期
通过日志验证ClassPathXmlBeanB
的依赖注入过程:
java
ApplicationContext context = new ClassPathXmlApplicationContext("classPathXml.xml");
ClassPathXmlBeanB beanB = context.getBean(ClassPathXmlBeanB.class);
log.info("BeanB的classPathXmlBeanA实例:{}", beanB.getClassPathXmlBeanA()); // 输出非null
日志显示,beanB
的classPathXmlBeanA
字段已被正确注入,说明依赖注入发生在finishBeanFactoryInitialization
阶段。
2.2 FileSystemXmlApplicationContext:文件系统XML的"灵活加载"
FileSystemXmlApplicationContext
与ClassPathXmlApplicationContext
的核心区别在于资源加载方式:
ClassPathXmlApplicationContext
使用ClassPathResource
,从类路径(src/main/resources
)加载XML。FileSystemXmlApplicationContext
使用FileSystemResource
,从文件系统的绝对路径加载XML(如/opt/config/app.xml
)。
2.2.1 适用场景与注意事项
-
适用场景:需要加载外部配置(如Docker容器挂载的配置文件)、多环境配置隔离(开发/生产环境不同路径)。
-
注意事项 :硬编码路径会导致部署不灵活,实际开发中建议通过
Environment
动态获取路径:
java
@Autowired
private Environment env;
public void init() {
String xmlPath = env.getProperty("custom.config.path", "classpath:default.xml");
ApplicationContext context = new FileSystemXmlApplicationContext(xmlPath);
}
三、Java注解配置的ApplicationContext:代码即配置的革命
3.1 AnnotationConfigApplicationContext:非Web环境的"零XML"
AnnotationConfigApplicationContext
支持通过@Configuration
和@Bean
注解定义Bean,彻底摆脱XML配置,是现代Spring应用的首选方式之一。
3.1.1 核心原理:AnnotatedBeanDefinitionReader
AnnotationConfigApplicationContext
内部使用AnnotatedBeanDefinitionReader
解析@Configuration
类,其核心逻辑如下:
- 扫描
@Configuration
类 :将标记了@Configuration
的类注册为Bean(默认名称为类名首字母小写)。 - 处理
@Bean
方法 :为每个@Bean
方法生成BeanDefinition
,类型为ConfigurationClassBeanDefinition
,并记录方法参数(用于依赖注入)。 - 处理其他注解 :如
@ComponentScan
(扫描@Component
、@Service
等注解)、@Import
(导入其他配置类)、@PropertySource
(加载属性文件)。
3.1.2 代码实战与生命周期验证
java
@Configuration
static class AnnotationConfig {
@Bean
public AnnotationConfigBeanA annotationConfigBeanA() {
log.info("创建 AnnotationConfigBeanA 实例");
return new AnnotationConfigBeanA();
}
@Bean
public AnnotationConfigBeanB annotationConfigBeanB(AnnotationConfigBeanA annotationConfigBeanA) {
log.info("创建 AnnotationConfigBeanB 实例,注入 AnnotationConfigBeanA");
return new AnnotationConfigBeanB(annotationConfigBeanA);
}
}
// 使用时:
ApplicationContext context = new AnnotationConfigApplicationContext(AnnotationConfig.class);
日志输出:
创建 AnnotationConfigBeanA 实例
创建 AnnotationConfigBeanB 实例,注入 AnnotationConfigBeanA
这说明@Bean
方法的调用顺序由依赖关系决定(annotationConfigBeanB
依赖annotationConfigBeanA
,因此先创建A再创建B)。
3.1.3 依赖注入的底层实现
@Bean
方法的参数注入由AutowiredAnnotationBeanPostProcessor
处理,其核心逻辑如下:
- 扫描方法参数 :遍历所有
@Bean
方法,收集需要注入的参数类型。 - 查找匹配的Bean :在容器中查找与参数类型匹配的Bean(支持
@Qualifier
指定名称)。 - 注入参数值 :将找到的Bean实例作为参数传递给
@Bean
方法。
四、Web环境下的ApplicationContext:集成Servlet容器的"企业级能力"
4.1 AnnotationConfigServletWebServerApplicationContext:Spring Boot的"Web引擎"
Spring Boot通过AnnotationConfigServletWebServerApplicationContext
简化了Web开发,其核心是集成Servlet容器 (如Tomcat)和自动配置Spring MVC。
4.1.1 核心组件解析
组件 | 作用 |
---|---|
ServletWebServerFactory |
创建Servlet容器(如TomcatServletWebServerFactory ) |
DispatcherServlet |
Spring MVC的前端控制器,负责请求分发 |
DispatcherServletRegistrationBean |
注册DispatcherServlet 到Servlet容器,配置映射路径(如/ ) |
RequestMappingHandlerMapping |
扫描@Controller 和@RequestMapping 注解,生成请求路径到处理方法的映射 |
4.1.2 启动流程详解(附调试日志)
java
@Configuration
static class AnnotationConfigServletWebServerConfig {
@Bean
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory(); // 默认端口8080
}
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
@Bean
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet) {
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
}
@Bean("/hello")
public Controller helloController() {
return (req, res) -> {
res.getWriter().println("Hello, Spring!");
return null;
};
}
}
===== Web 环境的 Java 配置 ApplicationContext =====
17:25:06.640 [main] INFO org.springframework.boot.web.embedded.tomcat.TomcatWebServer -- Tomcat initialized with port 8080 (http)
6月 09, 2025 5:25:06 下午 org.apache.coyote.AbstractProtocol init
信息: Initializing ProtocolHandler ["http-nio-8080"]
6月 09, 2025 5:25:06 下午 org.apache.catalina.core.StandardService startInternal
信息: Starting service [Tomcat]
6月 09, 2025 5:25:06 下午 org.apache.catalina.core.StandardEngine startInternal
信息: Starting Servlet engine: [Apache Tomcat/10.1.30]
6月 09, 2025 5:25:06 下午 org.apache.catalina.core.ApplicationContext log
信息: Initializing Spring embedded WebApplicationContext
17:25:06.925 [main] INFO org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext -- Root WebApplicationContext: initialization completed in 952 ms
6月 09, 2025 5:25:07 下午 org.apache.coyote.AbstractProtocol start
信息: Starting ProtocolHandler ["http-nio-8080"]
17:25:07.132 [main] INFO org.springframework.boot.web.embedded.tomcat.TomcatWebServer -- Tomcat started on port 8080 (http) with context path '/'
17:25:07.133 [main] INFO com.dwl.bean_factory_case.ApplicationContextCase -- Web ApplicationContext 已初始化,Servlet 容器已启动(默认端口 8080)
17:25:07.134 [main] INFO com.dwl.bean_factory_case.ApplicationContextCase -- 请访问 http://localhost:8080/hello 验证 Controller 是否生效(应输出 'Hello, Spring!')
4.1.3 DispatcherServlet的初始化与请求处理
DispatcherServlet
的初始化流程如下:
- 初始化WebApplicationContext :通过
WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext)
获取根上下文。 - 注册HandlerMapping :
RequestMappingHandlerMapping
扫描所有@Controller
,生成请求路径到处理方法的映射(存储在handlerMethods
中)。 - 注册HandlerAdapter :
RequestMappingHandlerAdapter
负责调用处理方法(支持@RequestBody
、@ResponseBody
等注解)。 - 启动Servlet容器 :
TomcatWebServer.start()
启动Tomcat,监听8080端口。
4.1.4 验证Controller生效
启动应用后访问http://localhost:8080/hello
,应返回Hello, Spring!
。
五、总结与最佳实践
5.1 不同ApplicationContext的对比
类型 | 核心特点 | 适用场景 |
---|---|---|
DefaultListableBeanFactory (手动) |
底层核心,需手动管理Bean定义 | 框架开发、自定义容器扩展 |
ClassPathXmlApplicationContext |
类路径XML配置,适合传统Spring项目 | 遗留系统迁移、明确分离配置与代码 |
FileSystemXmlApplicationContext |
文件系统XML配置,支持外部化配置 | 动态加载外部配置(如Docker挂载) |
AnnotationConfigApplicationContext |
Java注解驱动,代码即配置,无XML依赖 | 现代Spring/Spring Boot应用(非Web) |
AnnotationConfigServletWebServerApplicationContext |
集成Servlet容器,自动配置Spring MVC | Spring Boot Web应用(MVC、RESTful接口) |
5.2 最佳实践建议
- 优先使用Java注解配置:减少XML模板代码,提高可维护性(Spring Boot默认支持)。
- **Web环境使用
AnnotationConfigServletWebServerApplicationContext
**:利用Spring Boot自动配置,简化Servlet容器集成。 - 避免硬编码路径 :通过
Environment
动态获取配置路径,提升部署灵活性。 - 理解Bean生命周期 :合理使用
@PostConstruct
、InitializingBean
等回调,避免依赖未初始化的Bean。 - 监控Bean定义数量 :通过
context.getBeanDefinitionNames()
监控容器中的Bean,避免冗余定义。
总结
Spring ApplicationContext是Spring生态的"基石",其设计思想(IoC、DI、生命周期管理)贯穿整个Spring框架。下次遇到Spring启动异常、Bean注入失败等问题时,不妨从refresh()
方法的执行流程入手,逐步排查问题根源。
完整代码
java
package com.dwl.bean_factory_case;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.Controller;
/**
* @ClassName ApplicationContextCase
* @Description 本类演示 Spring 不同类型 ApplicationContext 的创建方式及底层原理,
* 涵盖 XML 配置、Java 注解配置(非 Web/Web),并深入解析 Spring IoC 容器的核心流程。
* <p>
* 核心目标:通过代码调用链,揭示 Spring 容器初始化、Bean 定义加载、依赖注入的底层实现。
* @Version 1.0.0
* @Date 2025
* @Author By Dwl
*/
@Slf4j
public class ApplicationContextCase {
public static void main(String[] args) {
// -------------------- 手动通过 BeanFactory 加载 XML 配置(底层原理演示) --------------------
manualLoadXmlWithBeanFactory();
// -------------------- 类路径 XML 配置的 ApplicationContext --------------------
classPathXmlApplicationContext();
// -------------------- 文件系统 XML 配置的 ApplicationContext --------------------
fileSystemXmlApplicationContext();
// -------------------- Java 注解配置的非 Web ApplicationContext --------------------
annotationConfigApplicationContext();
// -------------------- Java 注解配置的 Web ApplicationContext --------------------
annotationConfigServletWebServerApplicationContext();
}
/**
* 手动通过 DefaultListableBeanFactory 加载 XML 配置(演示 BeanFactory 底层原理)
* <p>
* 底层原理:Spring IoC 容器的核心是 DefaultListableBeanFactory,它通过 BeanDefinitionReader 解析配置,
* 将 XML 中的 <bean> 标签转换为 BeanDefinition 对象(Bean 的"元数据蓝图"),并注册到容器中。
*/
private static void manualLoadXmlWithBeanFactory() {
log.info("===== 手动加载 XML 配置(BeanFactory 底层原理) =====");
// 步骤1:初始化 DefaultListableBeanFactory(IoC容器核心)
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
log.info("初始化后 BeanDefinition 数量:{}", beanFactory.getBeanDefinitionCount()); // 输出0
// 步骤2:创建 XmlBeanDefinitionReader(XML配置解析器)
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
log.debug("XmlBeanDefinitionReader 初始化完成。");
// 步骤3:加载类路径XML配置(触发解析与注册)
Resource classPathResource = new ClassPathResource("classPathXml.xml");
try {
int count = reader.loadBeanDefinitions(classPathResource); // 核心方法
log.info("加载 classPathXml.xml 后 BeanDefinition 数量:{}(新增{}个)",
beanFactory.getBeanDefinitionCount(), count); // 输出2(假设XML有2个Bean)
} catch (BeanDefinitionStoreException e) {
log.error("加载类路径XML失败", e);
}
// 步骤4:加载文件系统XML配置(跨位置配置加载)
Resource fileSystemResource = new FileSystemResource("spring_case/src/main/resources/fileSystemXml.xml"); // 实际开发建议使用相对路径
try {
int count = reader.loadBeanDefinitions(fileSystemResource);
log.info("加载 fileSystemXml.xml 后 BeanDefinition 数量:{}(新增{}个)",
beanFactory.getBeanDefinitionCount(), count); // 输出4(累计新增2个)
} catch (BeanDefinitionStoreException e) {
log.error("加载文件系统XML失败", e);
}
}
/**
* 演示 ClassPathXmlApplicationContext(类路径 XML 配置的 ApplicationContext)
* <p>
* 底层原理:ClassPathXmlApplicationContext 是 ApplicationContext 的实现类,
* 内部封装了 DefaultListableBeanFactory,并通过 XmlBeanDefinitionReader 加载类路径 XML 配置。
* 其核心流程是调用 refresh() 方法完成容器初始化(实例化、依赖注入、初始化 Bean)。
*/
public static void classPathXmlApplicationContext() {
log.info("\n===== 类路径 XML 配置的 ApplicationContext =====");
// 1. 创建 ClassPathXmlApplicationContext(构造方法触发 refresh())
// 构造方法参数:XML 配置文件名(位于类路径下)
ApplicationContext context = new ClassPathXmlApplicationContext("classPathXml.xml");
// 2. 验证 Bean 定义已加载(输出所有 Bean 名称)
String[] beanDefinitionNames = context.getBeanDefinitionNames();
log.info("ClassPathXmlApplicationContext 中的 Bean 定义:");
for (String name : beanDefinitionNames) {
log.info(" - {}", name); // 输出 classPathXmlBeanA、classPathXmlBeanB
}
// 3. 验证依赖注入(Spring 自动注入 classPathXmlBeanA 到 classPathXmlBeanB)
ClassPathXmlBeanB beanB = context.getBean(ClassPathXmlBeanB.class);
log.info("验证依赖注入:classPathXmlBeanB 的 classPathXmlBeanA = {}", beanB.getClassPathXmlBeanA());
// 底层原理:Spring 在 Bean 初始化后,通过 AutowiredAnnotationBeanPostProcessor 扫描 @Autowired 注解,
// 从容器中查找匹配的 Bean(按类型)并注入到目标字段。
}
/**
* 演示 FileSystemXmlApplicationContext(文件系统 XML 配置的 ApplicationContext)
* <p>
* 底层原理:与 ClassPathXmlApplicationContext 类似,区别在于使用 FileSystemResource 加载文件系统绝对路径的 XML,
* 其他核心流程(refresh()、BeanDefinition 注册、依赖注入)完全一致。
*/
public static void fileSystemXmlApplicationContext() {
log.info("\n===== 文件系统 XML 配置的 ApplicationContext =====");
// 1. 创建 FileSystemXmlApplicationContext(显式指定文件系统路径)
ApplicationContext context = new FileSystemXmlApplicationContext(
"spring_case/src/main/resources/fileSystemXml.xml" // 注意:实际开发中建议使用相对路径或外部化配置
);
// 2. 验证 Bean 定义已加载
String[] beanDefinitionNames = context.getBeanDefinitionNames();
log.info("FileSystemXmlApplicationContext 中的 Bean 定义:");
for (String name : beanDefinitionNames) {
log.info(" - {}", name); // 输出 fileSystemXmlBeanA、fileSystemXmlBeanB
}
// 3. 验证依赖注入(逻辑与类路径 XML 一致)
FileSystemXmlBeanB beanB = context.getBean(FileSystemXmlBeanB.class);
log.info("验证依赖注入:fileSystemXmlBeanB 的 fileSystemXmlBeanA = {}", beanB.getFileSystemXmlBeanA());
}
/**
* 演示 AnnotationConfigApplicationContext(Java 注解配置的非 Web ApplicationContext)
* <p>
* 底层原理:AnnotationConfigApplicationContext 通过 AnnotatedBeanDefinitionReader 扫描 @Configuration 类,
* 解析其中的 @Bean 方法,生成 BeanDefinition 并注册到容器。其核心是"代码即配置",替代 XML。
*/
private static void annotationConfigApplicationContext() {
log.info("\n===== Java 注解配置的非 Web ApplicationContext =====");
// 1. 创建 AnnotationConfigApplicationContext(传入 @Configuration 类)
ApplicationContext context = new AnnotationConfigApplicationContext(AnnotationConfig.class);
// 2. 验证 Bean 定义已加载(包含 @Configuration 类本身和 @Bean 方法生成的 Bean)
String[] beanDefinitionNames = context.getBeanDefinitionNames();
log.info("AnnotationConfigApplicationContext 中的 Bean 定义:");
for (String name : beanDefinitionNames) {
log.info(" - {}", name); // 输出 annotationConfig(@Configuration 类)、annotationConfigBeanA、annotationConfigBeanB
}
// 3. 验证依赖注入(@Bean 方法参数自动注入)
AnnotationConfigBeanB beanB = context.getBean(AnnotationConfigBeanB.class);
log.info("验证依赖注入:annotationConfigBeanB 的 annotationConfigBeanA = {}", beanB.getAnnotationConfigBeanA());
// 底层原理:@Bean 方法的参数由 Spring 自动注入(按类型匹配容器中的 Bean),
// 本质是通过 AutowiredAnnotationBeanPostProcessor 处理方法参数的依赖。
}
/**
* 演示 AnnotationConfigServletWebServerApplicationContext(Web 环境的 Java 配置 ApplicationContext)
* <p>
* 底层原理:Spring Boot 为 Web 环境设计的上下文,集成 Servlet 容器(如 Tomcat)和 Spring MVC 组件。
* 核心流程:初始化 ServletWebServerFactory → 注册 DispatcherServlet → 启动 Web 服务器。
*/
private static void annotationConfigServletWebServerApplicationContext() {
log.info("\n===== Web 环境的 Java 配置 ApplicationContext =====");
// 1. 创建 AnnotationConfigServletWebServerApplicationContext(传入 Web 配置类)
new AnnotationConfigServletWebServerApplicationContext(AnnotationConfigServletWebServerConfig.class);
// 2. 验证 Web 组件已初始化(此处无法直接打印,需通过反射或日志观察)
log.info("Web ApplicationContext 已初始化,Servlet 容器已启动(默认端口 8080)");
// 3. (可选)验证 Controller 已注册(通过访问 /hello 接口)
log.info("请访问 http://localhost:8080/hello 验证 Controller 是否生效(应输出 'Hello, Spring!')");
}
// -------------------- 以下为演示用的 Bean 定义类 --------------------
/**
* 类路径 XML 配置中的 Bean A(无依赖)
*/
static class ClassPathXmlBeanA {
}
/**
* 类路径 XML 配置中的 Bean B(依赖 Bean A)
*/
@Data
static class ClassPathXmlBeanB {
private ClassPathXmlBeanA classPathXmlBeanA; // 由 XML 配置注入
}
/**
* 文件系统 XML 配置中的 Bean A(无依赖)
*/
static class FileSystemXmlBeanA {
}
/**
* 文件系统 XML 配置中的 Bean B(依赖 Bean A)
*/
@Data
static class FileSystemXmlBeanB {
private FileSystemXmlBeanA fileSystemXmlBeanA; // 由 XML 配置注入
}
/**
* Java 注解配置中的 Bean A(无依赖)
*/
static class AnnotationConfigBeanA {
}
/**
* Java 注解配置中的 Bean B(依赖 Bean A)
*/
@Data
static class AnnotationConfigBeanB {
private AnnotationConfigBeanA annotationConfigBeanA; // 由 @Bean 方法注入
}
// -------------------- 以下为 Java 配置类 --------------------
/**
* 非 Web 环境的 @Configuration 类(演示 Java 注解配置)
* <p>
* 底层原理:@Configuration 标记的类会被 AnnotatedBeanDefinitionReader 解析,
* 其中的 @Bean 方法会被转换为 BeanDefinition(factoryMethodName 为方法名,factoryBeanName 为配置类名)。
*/
@Configuration
static class AnnotationConfig {
/*
* @Bean 方法:声明一个 Bean(返回值类型为 BeanA)
* <p>
* 底层原理:Spring 会为该方法生成一个 BeanDefinition,类型为 ConfigurationClassBeanDefinition,
* 其 factoryBeanName 为当前配置类名("annotationConfig"),factoryMethodName 为方法名("annotationConfigBeanA")。
*/
@Bean
public AnnotationConfigBeanA annotationConfigBeanA() {
log.info("创建 AnnotationConfigBeanA 实例");
return new AnnotationConfigBeanA();
}
/*
* @Bean 方法:声明另一个 Bean(依赖 BeanA)
* <p>
* 底层原理:方法参数由 Spring 自动注入(按类型匹配容器中的 Bean),本质是通过 AutowiredAnnotationBeanPostProcessor
* 处理方法参数的依赖,从容器中查找匹配的 Bean 并注入。
*/
@Bean
public AnnotationConfigBeanB annotationConfigBeanB(AnnotationConfigBeanA annotationConfigBeanA) {
AnnotationConfigBeanB beanB = new AnnotationConfigBeanB();
log.info("创建 AnnotationConfigBeanB 实例,注入 AnnotationConfigBeanA");
beanB.setAnnotationConfigBeanA(annotationConfigBeanA);
return beanB;
}
}
/**
* Web 环境的 @Configuration 类(演示 Servlet 容器集成)
* <p>
* 底层原理:通过 @Bean 声明 ServletWebServerFactory、DispatcherServlet 等 Web 组件,
* Spring Boot 自动配置会触发 Servlet 容器的启动和 DispatcherServlet 的注册。
*/
@Configuration
static class AnnotationConfigServletWebServerConfig {
/*
* 声明 ServletWebServerFactory(Tomcat 容器工厂)
* <p>
* 底层原理:TomcatServletWebServerFactory 是 ServletWebServerFactory 的实现类,
* 负责创建 TomcatWebServer(管理 Tomcat 生命周期)。Spring Boot 默认端口为 8080,
* 可通过 @Bean 方法参数或 application.properties 自定义。
*/
@Bean
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory(); // 默认端口 8080
}
/*
* 声明 DispatcherServlet(Spring MVC 前端控制器)
* <p>
* 底层原理:DispatcherServlet 是 Spring MVC 的核心,负责请求分发。此处直接实例化,
* 后续通过 DispatcherServletRegistrationBean 注册到 Servlet 容器。
*/
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
/*
* 声明 DispatcherServlet 的注册信息(映射路径)
* <p>
* 底层原理:DispatcherServletRegistrationBean 继承 ServletRegistrationBean,
* 用于配置 Servlet 的名称、映射路径、初始化参数等。此处映射 "/" 表示拦截所有请求。
*/
@Bean
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet) {
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
}
/*
* 声明一个简单的 Controller(处理 /hello 请求)
* <p>
* 底层原理:@Bean 注解标记的 Controller 会被自动注册到 DispatcherServlet 的 HandlerMapping 中。
* RequestMappingHandlerMapping 扫描所有 @Controller 和 @RequestMapping 注解,生成请求路径到处理方法的映射。
*/
@Bean("/hello")
public Controller helloController() {
return (HttpServletRequest request, HttpServletResponse response) -> {
response.getWriter().println("Hello, Spring!");
return null; // 无视图,返回 null
};
}
}
// -------------------- 以下为模拟的 XML 配置内容(实际需创建对应 XML 文件) --------------------
/*
<!-- classPathXml.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="classPathXmlBeanA" class="com.dwl.bean_factory_case.ApplicationContextCase$ClassPathXmlBeanA"/>
<bean id="classPathXmlBeanB" class="com.dwl.bean_factory_case.ApplicationContextCase$ClassPathXmlBeanB">
<property name="classPathXmlBeanA" ref="classPathXmlBeanA"/>
</bean>
</beans>
<!-- fileSystemXml.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="fileSystemXmlBeanA" class="com.dwl.bean_factory_case.ApplicationContextCase$FileSystemXmlBeanA"/>
<bean id="fileSystemXmlBeanB" class="com.dwl.bean_factory_case.ApplicationContextCase$FileSystemXmlBeanB">
<property name="fileSystemXmlBeanA" ref="fileSystemXmlBeanA"/>
</bean>
</beans>
*/
}