Spring Boot 系统学习第三天:Spring依赖注入原理分析

1.概述

        Spring中关于依赖注入的代码实现非常丰富,涉及大量类和组件之间的协作与交互。从原理上讲,任何一个框架都存在一条核心执行流程,只要抓住这条主流程,就能把握框架的整体代码结构,Spring也不例外。无论采用何种依赖注入机制,前提都是Spring IoC容器正常启动。因此,IoC容器初始化就是我们理解和把握依赖注入实现机制的前提。

        本篇结合Bean的生命周期,把IoC容器初始化过程梳理成两大步骤,即Bean的注册和Bean的实例化。这两个步骤就构成了一条代码主流程。

2 Bean的注册

        在使用Spring时,可以通过获取一个应用上下文(ApplicationContext)对象来操作各种Bean,示例代码如下:

AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(ModulesWebApplication.class);

        这里的ApplicationContext接口代表的就是一个Spring IoC容器,而在Spring中存在一大批ApplicationContext接口的实现类。如果使用注解的配置方式,就可以使用上述代码中的AnnotationConfigApplicationContext来初始化容器上下文对象。刚开始阅读Spring源码时,建议直接从AnnotationConfigApplicationContext的启动流程切入,这一流程位于它的构造函数中,代码如下:

    public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
        this();
        //根据注解配置类注册Bean
        this.register(componentClasses);
        //刷新容器
        this.refresh();
    }

    public AnnotationConfigApplicationContext(String... basePackages) {
        this();
        //根据包路径配置扫描Bean
        this.scan(basePackages);
        //刷新容器
        this.refresh();
    }

        这两个构造函数的作用很明确,一个是根据注解配置类注册Bean,另一个则是根据包路径配置扫描Bean。这里以register()方法为例,来讨论Bean的注册过程。该方法源码如下:

    public void register(Class<?>... componentClasses) {
        Assert.notEmpty(componentClasses, "At least one component class must be specified");
        StartupStep registerComponentClass = this.getApplicationStartup().start("spring.context.component-classes.register").tag("classes", () -> {
            return Arrays.toString(componentClasses);
        });
        this.reader.register(componentClasses);
        registerComponentClass.end();
    }

        这里依赖AnnotationBeanDefinitionReader工具类来完成Bean的注册。AnnotationBeanDefinitionReader会遍历所有传入的annotatedClasses注解类,然后通过doRegisterBean()方法完成注册。

private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name, @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier, @Nullable BeanDefinitionCustomizer[] customizers) {
        //将注解配置类信息转换成一种BeanDefinition
        AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
        if (!this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
            abd.setInstanceSupplier(supplier);
            //获取Bean的作用域元数据,解析Bean作用域
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
            abd.setScope(scopeMetadata.getScopeName());
            //生成beanName
            String beanName = name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry);
            //解析AnnotationBeanDefinitionReader中的@Lazy,@Primary等注解
            AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
  
### Spring Boot依赖注入的字段注入方式 #### 字段注入示例 在 Spring Boot 中,`@Autowired` 是最常用的依赖注入方式之一。通过 `@Autowired` 注解可以直接将容器中的 Bean 自动注入到目标类的字段中。 以下是一个简单的字段注入示例: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class MyService { @Autowired private AnotherService anotherService; // 使用 @Autowired 进行字段注入 public void performAction() { anotherService.doSomething(); } } ``` 在这个例子中,`anotherService` 被标记为 `private` 并且未提供 setter 方法,Spring 容器仍然能够将其自动注入[^2]。 --- #### 字段注入的工作原理 字段注入的核心机制在于 SpringIoC(控制反转)容器。当 Spring 启动时,它会扫描所有的配置类以及带有特定注解(如 `@Component`, `@Service`, `@Repository` 等)的类,并将它们注册为 Bean。随后,在实例化这些 Bean 时,Spring 会在其生命周期的不同阶段调用一些扩展点接口来完成属性注入等工作。 具体来说,字段注入的过程涉及以下几个关键步骤: 1. **Bean 实例化** 当 Spring 创建一个新的 Bean 时,首先会调用无参构造函数或者工厂方法创建对象实例。 2. **属性填充** 接下来,Spring 将尝试解析并设置标注有 `@Autowired` 的字段。如果找到匹配的 Bean,则将其赋值给相应的字段[^3]。 3. **初始化回调** 属性注入完成后,Spring 可能还会触发其他扩展点逻辑,比如 `BeanPostProcessor.postProcessBeforeInitialization()` 和 `BeanPostProcessor.postProcessAfterInitialization()` 等。 需要注意的是,字段注入通常发生在 Bean 生命周期的早期阶段——即在任何自定义初始化逻辑之前就已经完成了依赖项的绑定操作。 --- #### 原理补充:基于反射的技术支持 上述过程实际上是借助 Java 的反射技术实现的。即使某些成员变量被声明为私有的 (`private`) ,只要允许访问权限修改 (accessible),那么就可以动态地为其分配新值而无需显式的 getter/setter 配合工作流程[^4]。 例如下面这段伪代码展示了如何利用反射去获取和设定一个非公共域的内容: ```java Field field = clazz.getDeclaredField("field"); field.setAccessible(true); field.set(instance, value); ``` 这种能力使得像 Spring Framework 这样的框架得以简化开发者体验的同时保持灵活性. --- ### 总结 综上所述,Spring Boot 提供了一种简洁高效的手段来进行依赖管理—那就是通过简单地标记几个关键字就能让整个应用架构变得更加模块化易于维护.[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

geminigoth

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值