源码分析:Spring IOC容器初始化过程

你好,我是猿java。

这篇文章,我们将通过剖析 Spring 5.x源码,深度分析 IOC 容器的初始化过程。

1 IOC 的基本概念

IOC,全称Inversion of Control,翻译为,它是一种设计原则,旨在通过减少对象之间的耦合度,提高系统的灵活性和可维护性。在传统的编程方式中,对象通常负责自己依赖的创建和管理,这导致了高耦合度。而在 IOC 模式下,对象的创建和依赖管理交由外部容器控制,实现了对象之间的松耦合。

Spring 的 IOC 容器负责管理应用程序中的对象及其依赖关系。它通过配置元数据(如 XML、注解、Java 配置类等)来描述对象的创建、装配和管理过程。IOC 容器在应用启动时,根据配置元数据创建和装配所有的 Bean,从而实现应用程序的依赖注入。

IOC 容器的核心接口包括:

  • BeanFactory :是 Spring IOC 容器的最基本接口,提供了获取 Bean 的基本功能。它延迟加载 Bean,即在第一次调用 getBean 方法时才创建 Bean。
  • ApplicationContext:继承自 BeanFactory,提供了更高级的功能,如国际化支持、事件传播、AOP 集成等。ApplicationContext 通常在企业级应用中使用更为广泛。

2. Spring IOC初始化流程

Spring 5.x 在 IOC 容器的初始化过程中,涵盖了配置解析、Bean 定义加载与注册、Bean 的实例化与装配、初始化以及后期处理等多个阶段。以下将对这些阶段进行详细解析。

2.1 配置元数据的解析

在 Spring 应用中,配置元数据描述了应用中各个 Bean 及其依赖关系。配置元数据可以通过多种方式提供,包括 XML 配置文件、注解以及 Java 配置类(基于 @Configuration 的类)。

2.1.1 XML 配置

传统的 Spring 配置方式,通过 XML 文件定义 Bean 及其依赖关系。Spring 通过 XmlBeanDefinitionReader 将 XML 文件解析为 BeanDefinition 对象,并注册到 BeanFactory 中。

示例 XML 配置:

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="myBean" class="com.yuanjava.MyBean">
        <property name="dependency" ref="myDependency"/>
    </bean>

    <bean id="myDependency" class="com.yuanjava.MyDependency"/>
</beans>

2.1.2 注解配置

Spring 提供了多种注解,用于定义 Bean 和管理依赖关系,如 @Component@Service@Repository@Controller@Configuration 以及 @Autowired 等。通过 ComponentScan 扫描包路径,容器自动检测和注册带有特定注解的类为 Bean。

示例注解配置:

java 复制代码
@Component
public class MyBean {
    @Autowired
    private MyDependency myDependency;
}

@Component
public class MyDependency { }

2.1.3 Java 配置

基于 Java 的配置方式,通过 @Configuration 注解的类,使用 @Bean 方法定义 Bean。这种方式结合了类型安全和灵活性,受到越来越多开发者的青睐。

示例 Java 配置:

java 复制代码
@Configuration
public class AppConfig {
    @Bean
    public MyBean myBean() {
        return new MyBean(myDependency());
    }

    @Bean
    public MyDependency myDependency() {
        return new MyDependency();
    }
}

2.2 Bean 定义的加载与注册

配置元数据被解析后,Spring IOC 容器需要将其转化为内部的 BeanDefinition 对象,并注册到 BeanFactory 中。BeanDefinition 包含了 Bean 的类名、作用域、初始化方法、销毁方法、依赖关系等信息。

在 Spring 5.x 中,具体步骤通常如下:

  1. 创建 BeanFactory 实例 :常用的实现类是 DefaultListableBeanFactory
  2. 使用 BeanDefinitionReader 读取配置 :如 XmlBeanDefinitionReaderAnnotatedBeanDefinitionReaderConfigurationClassPostProcessor 等。
  3. 解析并注册 BeanDefinition:将解析后的 Bean 定义注册到 BeanFactory 中。

示例代码:

java 复制代码
// 创建 BeanFactory 实例
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

// 创建 BeanDefinitionReader
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);

// 加载 XML 配置文件
reader.loadBeanDefinitions("classpath:applicationContext.xml");

// 或者使用注解配置
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

2.3 Bean 的实例化与装配

在 Bean 定义加载并注册后,IOC 容器根据需要实例化 Bean,并完成属性的注入与依赖的装配。Spring 提供了多种方式来完成 Bean 的实例化与装配,如构造函数注入、Setter 方法注入、注解注入等。

2.3.1 实例化策略

Spring 提供了多种 Bean 的实例化策略,包括:

  • 通过无参构造函数实例化:默认的实例化方式。
  • 通过工厂方法实例化:可以通过静态工厂方法或实例工厂方法来创建 Bean。
  • 通过构造函数参数实例化:支持通过构造函数参数传递依赖。

2.3.2 依赖注入方式

依赖注入分为两种主要方式:

  • 构造函数注入:通过构造函数传递依赖对象。

    java 复制代码
    public class MyBean {
        private final MyDependency myDependency;
    
        public MyBean(MyDependency myDependency) {
            this.myDependency = myDependency;
        }
    }
  • Setter 方法注入:通过 Setter 方法注入依赖对象。

java 复制代码
public class MyBean {
  private MyDependency myDependency;

  @Autowired
  public void setMyDependency(MyDependency myDependency) {
      this.myDependency = myDependency;
  }
}

在 Spring 5.x 中,推荐使用构造函数注入,因为它更符合不可变对象的设计理念,且有利于编写可测试的代码。

2.3.3 自动装配

Spring 支持自动装配,减少了显式配置的工作量。自动装配有以下几种模式:

  • 按类型自动装配 (@Autowired):根据 Bean 的类型进行装配。
  • 按名称自动装配 (@Qualifier) :结合 @Qualifier 注解指定 Bean 的名称。
  • 基于 Java 注解的装配 :如 @Primary@Resource 等。
  • 基于构造函数的自动装配:通过构造函数参数进行装配。

示例代码:

java 复制代码
@Component
public class MyService {
    private final MyRepository myRepository;

    @Autowired
    public MyService(MyRepository myRepository) {
        this.myRepository = myRepository;
    }
}

2.4 Bean 的初始化

在 Bean 被实例化并装配完成后,还需要进行初始化工作。初始化过程包括执行自定义的初始化方法、BeanPostProcessor 的前置和后置处理等。

2.4.1 InitializingBean 接口

Bean 可以通过实现 InitializingBean 接口,重写 afterPropertiesSet 方法来自定义初始化逻辑。

java 复制代码
public class MyBean implements InitializingBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        // 初始化逻辑
    }
}

2.4.2 自定义初始化方法

在 Bean 配置中,可以通过 init-method 属性指定自定义的初始化方法。

xml 复制代码
<bean id="myBean" class="com.yuanjava.MyBean" init-method="init"/>

或者通过注解 @PostConstruct 指定初始化方法:

java 复制代码
public class MyBean {
    @PostConstruct
    public void init() {
        // 初始化逻辑
    }
}

2.4.3 BeanPostProcessor

BeanPostProcessor 是 Spring 提供的扩展点,允许在 Bean 初始化前后进行自定义处理。常见的实现类有 AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessorProxyPostProcessor 等。

BeanPostProcessor 提供两个主要方法:

  • postProcessBeforeInitialization:在 Bean 初始化方法调用前执行。
  • postProcessAfterInitialization:在 Bean 初始化方法调用后执行。

示例代码:

java 复制代码
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // 初始化前处理
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // 初始化后处理
        return bean;
    }
}

2.5 Bean 的后处理与销毁

Bean 的生命周期不仅包括初始化,还包括销毁过程。Spring 提供了多种机制来处理 Bean 的销毁,如实现 DisposableBean 接口、指定销毁方法、使用 @PreDestroy 注解等。

2.5.1 DisposableBean 接口

通过实现 DisposableBean 接口,Bean 可以在销毁前执行特定的逻辑。

java 复制代码
public class MyBean implements DisposableBean {
    @Override
    public void destroy() throws Exception {
        // 销毁逻辑
    }
}

2.5.2 自定义销毁方法

在 Bean 配置中,可以通过 destroy-method 属性指定自定义的销毁方法。

xml 复制代码
<bean id="myBean" class="com.yuanjava.MyBean" destroy-method="cleanup"/>

或者使用 @PreDestroy 注解指定销毁方法:

java 复制代码
public class MyBean {
    @PreDestroy
    public void cleanup() {
        // 销毁逻辑
    }
}

2.5.3 DisposableBean 与 destroy-method 的优先级

当 Bean 同时实现了 DisposableBean 接口并指定了 destroy-method 时,Spring 会按照以下顺序执行销毁逻辑:

  1. 执行实现的 DisposableBean 接口的 destroy 方法。
  2. 执行 destroy-method 指定的方法。

这种方法确保了销毁逻辑的有序执行,且用户可以通过合理配置完成自定义的销毁操作。

3. 关键类与组件

在 Spring 5.x 中,IOC 容器的初始化过程涉及到多个关键类和组件,这些类和组件各司其职,共同完成容器的初始化与管理工作。以下将介绍其中几个重要的类和组件。

3.1 ApplicationContext 接口及其实现

ApplicationContext 是 Spring IOC 容器的核心接口,继承自 BeanFactory,提供了更强大的功能。常见的实现类包括:

  • ClassPathXmlApplicationContext:基于类路径的 XML 配置文件创建 ApplicationContext。
  • FileSystemXmlApplicationContext:基于文件系统的 XML 配置文件创建 ApplicationContext。
  • AnnotationConfigApplicationContext:基于 Java 注解的配置类创建 ApplicationContext。
  • GenericWebApplicationContext:适用于 Web 应用的泛化 ApplicationContext。

示例代码:

java 复制代码
// 基于 XML 配置
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

// 基于注解配置
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

3.2 DefaultListableBeanFactory

DefaultListableBeanFactoryBeanFactory 的默认实现,也是最常用的实现类之一。它支持 Bean 的定义注册、依赖注入、Bean 后处理、作用域管理等功能。

java 复制代码
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

3.3 BeanDefinition 与 BeanDefinitionReader

BeanDefinition 是 Spring 内部用于描述 Bean 的核心类,包含了 Bean 的类名、作用域、依赖关系、初始化方法等信息。

BeanDefinitionReader 是用于读取不同格式的配置元数据并注册到 BeanFactory 中的接口,常见的实现类有:

  • XmlBeanDefinitionReader:读取 XML 配置文件。
  • AnnotatedBeanDefinitionReader:读取基于注解的配置。
  • PropertiesBeanDefinitionReader:读取基于 properties 文件的配置。

示例代码:

java 复制代码
// 创建 BeanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

// 创建 XML BeanDefinitionReader
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);

// 加载 XML 配置
reader.loadBeanDefinitions("classpath:applicationContext.xml");

3.4 InstantiationStrategy

InstantiationStrategy 接口定义了 Bean 实例化的策略。Spring 提供了两种默认的实现:

  • SimpleInstantiationStrategy:简单的实例化策略,适用于多数场景。
  • CglibSubclassingInstantiationStrategy:使用 CGLIB 生成子类进行实例化,常用于需要 AOP 代理的 Bean。
java 复制代码
InstantiationStrategy strategy = new SimpleInstantiationStrategy();

3.5 AutowireCapableBeanFactory

AutowireCapableBeanFactory 是 BeanFactory 的子接口,提供了更高级别的功能,如支持自动装配、Bean 后处理等。它在 Spring 的自动装配和后处理机制中起到了关键作用。

java 复制代码
AutowireCapableBeanFactory autowireCapableBeanFactory = context.getAutowireCapableBeanFactory();

3.6 BeanPostProcessor

BeanPostProcessor 是 Spring 提供的扩展点,用于在 Bean 的初始化前后进行自定义处理。常用的实现类包括:

  • AutowiredAnnotationBeanPostProcessor :处理 @Autowired 注解的装配。
  • CommonAnnotationBeanPostProcessor :处理 @PostConstruct@PreDestroy 注解。
  • ProxyPostProcessor:用于生成 AOP 代理等。
java 复制代码
public class MyCustomBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // 自定义前置处理
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // 自定义后置处理
        return bean;
    }
}

4. 总结

本文,我们通过源码深度分析了 Spring 5.x IOC容器的启动流程,IOC是 Spring的核心,也是比较难懂的一部分,建议可以多去阅读 Spring源码,了解其精髓。

5. 学习交流

如果你觉得文章有帮助,请帮忙转发给更多的好友,或关注公众号:猿java,持续输出硬核文章。

相关推荐
luckyext18 分钟前
Postman发送GET请求示例及注意事项
前端·后端·物联网·测试工具·小程序·c#·postman
架构文摘JGWZ32 分钟前
SQLite?低调不是小众...
数据库·后端·学习·sqlite
知识浅谈1 小时前
@Validate 注解的使用-分组案例很有用
java·springboot
uhakadotcom1 小时前
Pandas DataFrame 入门教程
后端·面试·github
Trouvaille ~1 小时前
【Java篇】一法不变,万象归一:方法封装与递归的思想之道
java·开发语言·面向对象·javase·递归·方法·基础入门
Zhava1 小时前
MybatisPlus中的customSqlSegment动态拼接where条件
java·mybatis
Asthenia04121 小时前
深入理解 Java 中的 ThreadLocal:从传统局限到 TransmittableThreadLocal 的解决方案
后端
极客先躯1 小时前
高级java每日一道面试题-2025年2月26日-框架篇[Mybatis篇]-Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式 ?
java·mybatis·嵌套映射·resulttype属性·resultmap属性·results注解·列名别名
勇哥java实战分享1 小时前
一次非常典型的 JVM OOM 事故 (要注意 where 1 = 1 哦)
后端
Asthenia04122 小时前
ThreadLocal原理分析
后端