当前位置: 首页 > article >正文

Spring 核心技术解析【纯干货版】- II:Spring 基础模块 Spring-Beans 模块精讲

在现代软件开发中,依赖注入和控制反转已经成为了架构设计中不可或缺的模式。而 Spring 框架,作为 Java 领域最流行的开发框架之一,提供了一整套强大而灵活的机制来实现这些模式。Spring-Beans 模块是 Spring 框架中最基础和核心的模块之一,负责管理应用程序中的 Bean(即对象)的生命周期和依赖关系。

本篇文章将深入解析 Spring-Beans 模块的核心概念和功能,帮助开发者全面理解 Spring 框架中的 Bean 定义、管理和生命周期管理等关键机制。通过详细的代码示例,我们将探索如何通过 Spring-Beans 来实现高效的依赖注入,如何管理 Bean 的生命周期,以及如何解析 XML 配置文件等重要技术细节。


文章目录

      • 1、Spring-Beans 模块概述
        • 1.1、Spring-Beans 模块介绍
        • 1.2、Spring-Beans 模块作用
      • 2、Spring-Beans 提供的对 Bean 的定义和管理
        • 2.1、BeanDefinition
        • 2.2、BeanFactory
      • 3、Spring-Beans 提供的生命周期管理方式
        • 3.1、doCreateBean 方法
        • 3.2、实例化 Bean
        • 3.3、属性注入
        • 3.4、初始化 Bean
        • 3.5、代理处理
      • 4、Spring-Beans 提供的解析 XML 配置文件的方式
      • X、后记


1、Spring-Beans 模块概述

1.1、Spring-Beans 模块介绍

Spring-Beans 模块,是 Spring 核心容器模块之一。它主要负责 Bean(即应用程序中的对象)的定义、配置、创建、管理和销毁。Spring Beans 模块通过依赖注入(Dependency Injection, DI)和控制反转(Inversion of Control, IoC)模式帮助开发者解耦对象之间的依赖关系,从而简化代码结构,提高可测试性和可维护性。

Spring-Beans 的核心实体是 BeanDefinitionBeanFactory。前者映射我们的定义,后者则是依据定义生产 bean 的工厂。

1.2、Spring-Beans 模块作用

在作者看来 Spring-Beans 模块主要提供的功能大致可以分为 3 块:

  1. 提供对 Bean 的定义和管理:Spring-Beans 提供了 BeanDefinition 来描述 Bean,用 BeanFactory 来管理 Bean。
  2. 提供对 Bean 生命周期的管理方式:Spring-Beans 在 AbstractAutowireCapableBeanFactory 类中使用 doCreateBean 方法创建 Bean,其过程就是最基本的 Bean 的生命周期。
  3. 提供解析 XML 配置文件的方式:Spring-Beans 模块提供了可以将 Bean 从 XML 配置中进行解析出来的方式,

2、Spring-Beans 提供的对 Bean 的定义和管理

2.1、BeanDefinition

在上一篇关于 Spring-Core 的模块解析文章中,提到了 Spring-Core 实现了将 Class 文件转换为抽象 Resource,并使用 ASM 获取 Class 元数据的方式。而在 Spring-Beans 中,则进一步解析元数据,提取 Bean 的定义信息,并封装到 BeanDefinition 中。

换句话说,Spring 容器管理一个 Bean 或多个 Bean,这些 Bean 是通过我们提供给容器的配置元数据被创建出来,在容器中,这些 Bean 的定义用 BeanDefinition 对象来表示(BeanDefinition 就是用来描述一个 Bean 或者 BeanDefinition 就是 Bean 的定义)。

BeanDefinition 通常包含以下元数据:

  • 全限定类名, 通常是 Bean 的实际实现类;
  • Bean 行为配置元素,它们说明 Bean 在容器中的行为(作用域、生命周期回调等等);
  • Bean 执行工作所需要的的其他 Bean 的引用,这些 Bean 也称为协作者或依赖项;
  • 其他配置信息,例如,管理连接池的 Bean 中,限制池的大小或者使用的连接的数量。
2.2、BeanFactory

有了对 Bean 的定义,自然还需要有对 Bean 的管理。而 BeanFactory 就是一个为存储和管理 Spring Bean 提供基本功能的 IOC 容器。

从具体实现来说,BeanFactory 接口是整个模块的核心接口,几乎所有功能都是围绕对象展开的。BeanFactory 提供了创建对象的功能,并对一部分对象进行管理,这是控制反转的基础。此外,在创建对象的过程中完成依赖注入,这时我们可以说 BeanFactory 是一个典型的 IOC 容器,它提供了基本的 IoC(控制反转)功能,是所有 Spring 应用的基石。

使用的简单示例:

import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;

public class BeanFactoryExample {

    public static void main(String[] args) {
        // 创建一个 BeanFactory
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

        // 创建 BeanDefinition
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        // 设置 Bean 的类型
        beanDefinition.setBeanClass(MyBean.class);
        // 注册 BeanDefinition 到工厂
        factory.registerBeanDefinition("myBean", beanDefinition);

        // 获取 Bean 实例
        MyBean myBean = (MyBean) factory.getBean("myBean");
        // 使用 Bean
        // 输出: Hello, Default Message
        myBean.sayHello(); 
        // 修改属性值
        myBean.setMessage("Spring Bean Factory");
        // 输出: Hello, Spring Bean Factory
        myBean.sayHello(); 
    }

    static class MyBean {
        private String message;
        public MyBean() {
            this.message = "Default Message";
        }
        public void setMessage(String message) {
            this.message = message;
        }
        public void sayHello() {
            System.out.println("Hello, " + message);
        }
    }
}

3、Spring-Beans 提供的生命周期管理方式

我们都知道 Spring Bean 具有生命周期,而这生命周期的能力就是在 Spring-Beans 模块这里提供的。 Spring-Beans 模块提供了一个名为 AbstractAutowireCapableBeanFactory 的抽象类。

AbstractAutowireCapableBeanFactory 是 Spring 框架中至关重要的核心类之一,承担着整个 Bean 生命周期关键步骤的管理责任。其在 Bean 实例化、属性注入、初始化及代理处理等关键环节中发挥着关键作用,贯穿了整个 Bean 的声明周期。

3.1、doCreateBean 方法

如果深入研究 AbstractAutowireCapableBeanFactory 源码,将关注其中一个核心方法:doCreateBean。这个方法是贯穿 Bean 生命周期的纽带,涵盖了实例化、属性注入、初始化以及代理处理等关键任务。

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
    // 尝试从缓存中移除已创建的单例实例,如果是单例 Bean
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
        instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName);
    }

    // 如果缓存中没有实例化的 Bean,则调用 createBeanInstance 创建新的实例
    if (instanceWrapper == null) {
        instanceWrapper = this.createBeanInstance(beanName, mbd, args);
    }

    // 获取实例化后的 Bean 对象和类型
    Object bean = instanceWrapper.getWrappedInstance();
    Class<?> beanType = instanceWrapper.getWrappedClass();

    // 如果 Bean 类型不为 NullBean,则记录解析后的目标类型
    if (beanType != NullBean.class) {
        mbd.resolvedTargetType = beanType;
    }

    // 同步操作,确保在处理过程中 BeanDefinition 只有一个线程在操作
    synchronized(mbd.postProcessingLock) {
        if (!mbd.postProcessed) {
            try {
                // 应用 BeanDefinition 的后处理器(例如 @Configuration 注解的类的处理)
                this.applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
            } catch (Throwable var17) {
                // 如果后处理失败,抛出 BeanCreationException
                Throwable ex = var17;
                throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex);
            }

            // 标记 BeanDefinition 已经处理过了
            mbd.postProcessed = true;
        }
    }

    // 如果是单例 Bean 并且容器允许循环引用,则尝试提前缓存 Bean 实例,解决循环依赖
    boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName);
    if (earlySingletonExposure) {
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references");
        }

        // 提前将 Bean 工厂放入缓存中,防止循环引用时直接获取到未完全初始化的实例
        this.addSingletonFactory(beanName, () -> {
            return this.getEarlyBeanReference(beanName, mbd, bean);
        });
    }

    Object exposedObject = bean;

    try {
        // 完成依赖注入(填充 Bean 的属性)
        this.populateBean(beanName, mbd, instanceWrapper);

        // 完成 Bean 的初始化操作(例如调用 @PostConstruct 标注的方法)
        exposedObject = this.initializeBean(beanName, exposedObject, mbd);
    } catch (Throwable var18) {
        // 如果初始化过程中出现异常,抛出 BeanCreationException
        Throwable ex = var18;
        if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException)ex).getBeanName())) {
            throw (BeanCreationException)ex;
        }

        // 如果初始化失败,抛出异常
        throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
    }

    // 如果提前暴露了单例对象,需要检查是否是早期的引用
    if (earlySingletonExposure) {
        // 尝试获取早期引用
        Object earlySingletonReference = this.getSingleton(beanName, false);
        if (earlySingletonReference != null) {
            // 如果实际对象还是原始对象,则用早期引用替代
            if (exposedObject == bean) {
                exposedObject = earlySingletonReference;
            } else if (!this.allowRawInjectionDespiteWrapping && this.hasDependentBean(beanName)) {
                // 如果有依赖的 Bean 还在等待注入,检测并抛出循环依赖异常
                String[] dependentBeans = this.getDependentBeans(beanName);
                Set<String> actualDependentBeans = new LinkedHashSet(dependentBeans.length);
                for (String dependentBean : dependentBeans) {
                    // 如果依赖 Bean 未被创建,则加入集合
                    if (!this.removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                        actualDependentBeans.add(dependentBean);
                    }
                }

                // 如果依赖集合不为空,抛出循环依赖异常
                if (!actualDependentBeans.isEmpty()) {
                    throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean.");
                }
            }
        }
    }

    try {
        // 注册 Bean 的销毁方法(例如 DisposableBean 或 @PreDestroy 标注的方法)
        this.registerDisposableBeanIfNecessary(beanName, bean, mbd);
        return exposedObject; // 返回最终暴露的 Bean 实例
    } catch (BeanDefinitionValidationException var16) {
        // 如果销毁方法签名无效,抛出异常
        throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", var16);
    }
}
3.2、实例化 Bean

在这一步中,doCreateBean 方法首先尝试从单例缓存中获取 Bean 的包装实例,以支持单例模式。如果未找到,它将通过 createBeanInstance 方法进行实例化。

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
    // 尝试从缓存中移除已创建的单例实例,如果是单例 Bean
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
        instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName);
    }

    // 如果缓存中没有实例化的 Bean,则调用 createBeanInstance 创建新的实例
    if (instanceWrapper == null) {
        instanceWrapper = this.createBeanInstance(beanName, mbd, args);
    }
  
  	...
}
3.3、属性注入

populateBean 方法负责将属性注入到 Bean 实例中,包括将依赖项注入到 Bean 中,以建立 Bean 之间的关系。

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
    ...
		// 完成依赖注入(填充 Bean 的属性)
    this.populateBean(beanName, mbd, instanceWrapper);
  	...
}
3.4、初始化 Bean

在初始化 Bean 这一步,initializeBean 方法被调用,负责执行 Bean 的初始化。这包括调用任何与 Bean 生命周期相关的回调方法,以及应用任何与 Bean 相关的后处理器。

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
    ...
    // 完成 Bean 的初始化操作(例如调用 @PostConstruct 标注的方法)
    exposedObject = this.initializeBean(beanName, exposedObject, mbd);
  	...
}
3.5、代理处理

在初始化之后,如果存在代理,通过从二级缓存获取早期半成品对象,实现代理的替换。这对于 AOP 等方面的处理非常关键。

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
    ...
    // 如果提前暴露了单例对象,需要检查是否是早期的引用
    if (earlySingletonExposure) {
        // 尝试获取早期引用
        Object earlySingletonReference = this.getSingleton(beanName, false);
        if (earlySingletonReference != null) {
            // 如果实际对象还是原始对象,则用早期引用替代
            if (exposedObject == bean) {
                exposedObject = earlySingletonReference;
            }
          	...
        }
      	...
    }
  	...
}

4、Spring-Beans 提供的解析 XML 配置文件的方式

在 Spring 中,XML 文件 主要用于配置 Bean 定义,这些文件会被 Spring 读取并解析,然后根据这些解析出的元数据来管理 Bean。这一功能的实现就是在 Spring-Beans 模块中完成的。

整个过程实际上分为两个部分:

  1. 读取 XML 配置文件(通过文件系统、类路径等方式读取 XML 文件)。
  2. 解析 XML 内容(使用 XML 解析器将 XML 内容转换为 Java 对象,如 BeanDefinition)并将其加载到 Spring 容器中。

Spring-Beans 模块提供了解析 XML 内容并将其加载到 Spring 容器中的方式。XmlBeanDefinitionReader 是 Spring 用于读取 XML 配置文件并解析 Bean 定义的核心类,它继承自 AbstractBeanDefinitionReader 类,并实现了 BeanDefinitionReader 接口。

XmlBeanDefinitionReader 主要职责:

  • 解析 XML 文件:通过 XML 解析机制(基于 DOM 或 SAX)读取 XML 配置文件。
  • 将 XML 转换为 BeanDefinition:它负责将 XML 配置文件中的信息转换为 BeanDefinition 对象,这些对象用于描述 Bean 的元数据。
  • 注册 BeanDefinition:一旦解析完成,它会将这些 BeanDefinition 注册到 Spring 容器的 BeanFactory(通常是 DefaultListableBeanFactory)中。

使用示例:

准备 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">

    <!-- 定义一个名为 car 的 Bean -->
    <bean id="car" class="com.example.Car">
        <property name="model" value="Tesla Model S"/>
        <property name="year" value="2023"/>
    </bean>

</beans>

创建 Bean 类:假设我们有一个 Car 类,它包含 modelyear 两个属性,并且有相应的 getter 和 setter 方法:

package com.example;

public class Car {
    private String model;
    private int year;

    public String getModel() {
        return model;
    }

    public void setModel(String model) {
        this.model = model;
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    @Override
    public String toString() {
        return "Car{model='" + model + "', year=" + year + "}";
    }
}

使用 XmlBeanDefinitionReader 加载 XML 配置:XmlBeanDefinitionReader 负责读取并解析 XML 文件,将其中的 Bean 定义转换为 BeanDefinition 对象,并将它们注册到 Spring 容器中。

import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.beans.factory.BeanDefinition;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

public class Main {
    public static void main(String[] args) {
        // 创建一个 BeanFactory 实例
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        
        // 创建一个 XmlBeanDefinitionReader 实例
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        
        // 通过 ClassPathResource 加载 XML 配置文件
        Resource resource = new ClassPathResource("beans.xml");
        
        // 加载并解析 XML 配置文件
        int beanCount = reader.loadBeanDefinitions(resource);
        
        System.out.println("Total Beans loaded: " + beanCount);
        
        // 获取并使用注册的 Bean
        Car car = (Car) beanFactory.getBean("car");
        System.out.println(car);
    }
}

X、后记

通过本篇文章的讲解,相信大家对于 Spring-Beans 模块的核心概念有了更深刻的理解。Spring-Beans 的依赖注入和生命周期管理为我们的应用提供了更高的可维护性、可扩展性和可测试性。在实际开发过程中,掌握 Spring-Beans 的核心技术,不仅能够帮助我们更好地构建应用,还能提高代码的解耦度和开发效率。

Spring-Beans 模块的功能强大而灵活,但也需要开发者通过不断的实践来深入掌握。希望本篇文章能为大家在使用 Spring 框架时提供一些帮助,也欢迎大家持续关注 Spring 框架的最新动态和最佳实践,共同进步。


http://www.kler.cn/a/463055.html

相关文章:

  • Niushop商城商业插件_cps联盟_包装转换_视频购物_同城配送_上门预约等插件的安装方法
  • 信息科技伦理与道德1:研究方法
  • CentOS — 目录管理
  • MinGW 和 MinGW-w64 的介绍与配置
  • docker 安装influxdb
  • 数据挖掘——支持向量机分类器
  • pyside6总结
  • 网络编程原理:回显服务器与客户端通信交互功能
  • Day20:逻辑运算
  • 30.Marshal.AllocHGlobal C#例子
  • 递归算法.
  • AI对接之JSON Output
  • 使用连字符容易出错,尽量使用驼峰式的
  • java 上传txt json等类型文件解析后返回给前端
  • OpenCV-Python实战(9)——滤波降噪
  • C++“STL之String”
  • 说说缓存使用的具体场景都有哪些?缓存和数据库一致性问题该如何解决?缓存使用常见问题有哪些?
  • 融合表面信息和等变扩散的分子对接模型 SurfDock - 评测
  • Git的使用流程(详细教程)
  • NFT Insider #162:Cool Cats和Doodles或将推出代币
  • 线性表的三种常见查找算法(顺序查找、折半查找、分块查找)及算法分析
  • 无人机巡检在光伏电站中的应用优势
  • HarmonyOS NEXT版本Stage应用开发模型介绍(附视频讲解)
  • SWM221系列芯片之电机应用及控制
  • git的全通路线介绍
  • Mono里运行C#脚本19—get_runtime_by_version