Spring不同类型的ApplicationContext的创建方式

引言

Spring框架的核心是控制反转(IoC)​依赖注入(DI)​ ,而承载这一核心的正是ApplicationContext接口及其实现类。无论是传统的XML配置时代,还是现代的Java注解与自动配置时代,ApplicationContext始终是Spring应用的"神经中枢",负责Bean的生命周期管理、资源加载、事件传播等关键功能。

本文将通过5大实战场景 ​(手动BeanFactory加载、类路径/文件系统XML配置、Java注解配置、Web环境集成),结合源码级解析调试日志验证,从底层原理到实战应用,掌握Spring ApplicationContext的核心机制。

一、手动通过BeanFactory加载XML配置:IoC容器的"最小可用单元"

1.1 底层核心类解析

要理解Spring IoC的底层原理,必须从DefaultListableBeanFactoryXmlBeanDefinitionReader入手:

1.1.1 DefaultListableBeanFactory:IoC容器的"心脏"

DefaultListableBeanFactory是Spring IoC容器的基础实现类,内部维护了两大核心数据结构:

  • beanDefinitionMapConcurrentHashMap<String, BeanDefinition>,存储所有Bean的元数据(类、构造参数、属性、作用域等)。
  • singletonObjectsConcurrentHashMap<String, Object>,单例Bean的缓存(Spring默认作用域为单例)。

其他关键属性包括:

  • beanDefinitionNames:所有Bean定义的名称列表(用于遍历)。
  • autowireCapableBeanFactory:支持自动装配的Bean工厂接口(处理依赖注入)。
1.1.2 XmlBeanDefinitionReader:XML配置的"翻译官"

XmlBeanDefinitionReader负责将XML文件中的<bean>标签转换为BeanDefinition对象。其核心方法是loadBeanDefinitions(Resource resource),内部调用parseBeanDefinitions解析XML,并通过registerBeanDefinitionBeanDefinition注册到DefaultListableBeanFactorybeanDefinitionMap中。


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)的核心逻辑如下:

  1. 获取XML输入流 ​:通过Resource.getInputStream()读取XML文件内容。

  2. 解析XML文档 ​:使用DocumentBuilderFactory解析XML,得到Document对象。

  3. 遍历<bean>标签 ​:对每个<bean>标签,解析idclassscopeproperty等属性。

  4. 生成BeanDefinition​:

    • BeanDefinitionBuilder根据标签属性构建GenericBeanDefinition(最常见的BeanDefinition实现类)。
    • 设置Bean的类(setBeanClass(Class<?> beanClass))、作用域(setScope(String scope))、构造参数(setConstructorArgumentValues)、属性值(setPropertyValues)等。
  5. 注册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对象,其中classPathXmlBeanBpropertyValues属性包含对classPathXmlBeanA的引用。


二、类路径与文件系统XML配置的ApplicationContext

2.1 ClassPathXmlApplicationContext:类路径XML的"开箱即用"

ClassPathXmlApplicationContextApplicationContext的实现类,其构造方法默认调用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执行:

    1. 获取Bean定义(getBeanDefinition)。
    2. 确定Bean的作用域(单例/原型/请求等)。
    3. 创建Bean实例(通过反射调用无参构造器,或工厂方法)。
    4. 依赖注入(处理@Autowired@Resource注解,或XML中的<property>标签)。
    5. 初始化Bean(调用InitializingBean.afterPropertiesSet()init-method方法)。
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

日志显示,beanBclassPathXmlBeanA字段已被正确注入,说明依赖注入发生在finishBeanFactoryInitialization阶段。

2.2 FileSystemXmlApplicationContext:文件系统XML的"灵活加载"

FileSystemXmlApplicationContextClassPathXmlApplicationContext的核心区别在于资源加载方式​:

  • 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类,其核心逻辑如下:

  1. 扫描@Configuration :将标记了@Configuration的类注册为Bean(默认名称为类名首字母小写)。
  2. 处理@Bean方法 :为每个@Bean方法生成BeanDefinition,类型为ConfigurationClassBeanDefinition,并记录方法参数(用于依赖注入)。
  3. 处理其他注解 :如@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处理,其核心逻辑如下:

  1. 扫描方法参数 :遍历所有@Bean方法,收集需要注入的参数类型。
  2. 查找匹配的Bean :在容器中查找与参数类型匹配的Bean(支持@Qualifier指定名称)。
  3. 注入参数值 :将找到的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的初始化流程如下:

  1. 初始化WebApplicationContext :通过WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext)获取根上下文。
  2. 注册HandlerMappingRequestMappingHandlerMapping扫描所有@Controller,生成请求路径到处理方法的映射(存储在handlerMethods中)。
  3. 注册HandlerAdapterRequestMappingHandlerAdapter负责调用处理方法(支持@RequestBody@ResponseBody等注解)。
  4. 启动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 最佳实践建议

  1. 优先使用Java注解配置:减少XML模板代码,提高可维护性(Spring Boot默认支持)。
  2. **Web环境使用AnnotationConfigServletWebServerApplicationContext**:利用Spring Boot自动配置,简化Servlet容器集成。
  3. 避免硬编码路径 :通过Environment动态获取配置路径,提升部署灵活性。
  4. 理解Bean生命周期 :合理使用@PostConstructInitializingBean等回调,避免依赖未初始化的Bean。
  5. 监控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>
    */
}
相关推荐
你的人类朋友4 分钟前
✍️Node.js CMS框架概述:Directus与Strapi详解
javascript·后端·node.js
面朝大海,春不暖,花不开23 分钟前
自定义Spring Boot Starter的全面指南
java·spring boot·后端
得过且过的勇者y23 分钟前
Java安全点safepoint
java
颜颜yan_1 小时前
【HarmonyOS5】UIAbility组件生命周期详解:从创建到销毁的全景解析
架构·harmonyos·鸿蒙·鸿蒙系统
钡铼技术ARM工业边缘计算机1 小时前
【成本降40%·性能翻倍】RK3588边缘控制器在安防联动系统的升级路径
后端
夜晚回家1 小时前
「Java基本语法」代码格式与注释规范
java·开发语言
斯普信云原生组1 小时前
Docker构建自定义的镜像
java·spring cloud·docker
wangjinjin1801 小时前
使用 IntelliJ IDEA 安装通义灵码(TONGYI Lingma)插件,进行后端 Java Spring Boot 项目的用户用例生成及常见问题处理
java·spring boot·intellij-idea
wtg44521 小时前
使用 Rest-Assured 和 TestNG 进行购物车功能的 API 自动化测试
java
CryptoPP2 小时前
使用WebSocket实时获取印度股票数据源(无调用次数限制)实战
后端·python·websocket·网络协议·区块链