Spring DI(依赖注入)全过程详解

依赖注入(Dependency Injection, DI)是Spring框架的核心特性之一,它实现了控制反转(IoC)原则,让对象之间的依赖关系由Spring容器管理,而不是由对象自己创建或查找依赖对象。下面我将详细介绍Spring DI的完整工作过程。

图片[1]_Spring DI(依赖注入)全过程详解_知途无界

一、DI基本概念

依赖注入是指容器负责创建对象并管理它们的依赖关系,对象只需要声明它需要什么依赖,而不需要关心这些依赖是如何创建和管理的。

Spring支持三种主要的依赖注入方式:

  1. 构造器注入(Constructor Injection)​
  2. Setter注入(Setter Injection)​
  3. 字段注入(Field Injection)​​(通过@Autowired直接标注字段)

二、DI全过程详解

1. 组件扫描与Bean定义

过程​:

  • Spring容器启动时,会扫描指定的包路径(通过@ComponentScan注解)
  • 发现带有@Component@Service@Repository@Controller等注解的类
  • 为这些类创建Bean定义(BeanDefinition),记录类的元数据信息

关键点​:

  • 这些类将成为Spring管理的Bean
  • 容器知道这些类的存在及其依赖关系

2. Bean实例化

过程​:

  • 当应用需要某个Bean时,Spring容器会根据Bean定义创建该Bean的实例
  • 实例化过程通常通过反射调用类的无参构造函数完成

关键点​:

  • 默认情况下,Bean是单例的(Singleton),即容器中只有一个实例
  • 也可以配置为原型(Prototype)、请求(Request)、会话(Session)等作用域

3. 依赖解析与注入

这是DI过程的核心环节,分为以下几个步骤:

(1) 依赖发现

过程​:

  • Spring分析Bean的构造函数、setter方法或字段,寻找需要注入的依赖
  • 通过注解(如@Autowired@Resource@Inject)或配置确定依赖关系

注入方式分析​:

  1. 构造器注入​:通过类的构造函数参数识别依赖
  2. Setter注入​:通过setter方法参数识别依赖
  3. 字段注入​:直接通过字段识别依赖(不推荐)

(2) 依赖查找

过程​:

  • 对于每个发现的依赖,Spring在容器中查找匹配的Bean
  • 查找过程考虑Bean的类型和限定符(如@Qualifier指定的值)
  • 如果找到多个同类型Bean,需要通过@Primary@Qualifier解决歧义

关键点​:

  • Spring首先尝试按类型匹配
  • 如果按类型找到多个Bean,则按名称匹配或使用限定符

(3) 依赖注入

过程​:

  • 找到匹配的依赖Bean后,Spring将其注入到目标Bean中
  • 注入方式取决于注入类型:
    • 构造器注入​:通过反射调用构造函数并传入依赖
    • Setter注入​:通过反射调用setter方法设置依赖
    • 字段注入​:通过反射直接设置字段值

关键点​:

  • 注入过程是递归的,如果依赖的Bean本身也有依赖,Spring会先解析这些依赖
  • 整个过程形成了一个依赖关系图,Spring确保依赖关系正确解析

4. 初始化与后处理

过程​:

  • 依赖注入完成后,Spring会调用Bean的初始化方法
  • 可以通过@PostConstruct注解、实现InitializingBean接口或配置init-method指定初始化方法

后处理器作用​:

  • Spring的Bean后处理器(BeanPostProcessor)在此阶段可以对Bean进行额外处理
  • 例如:AOP代理创建、属性填充等

5. Bean就绪可用

过程​:

  • 经过上述所有步骤后,Bean完全初始化并准备好被使用
  • 当应用代码请求该Bean时,Spring直接返回已创建并注入依赖的实例

三、DI工作过程详细流程图

Spring容器启动
    │
    ├── 扫描包路径,发现@Component等注解的类
    │       │
    │       └── 为每个类创建Bean定义(BeanDefinition)
    │
    └── 准备Bean工厂,等待Bean请求
            │
            └── 当应用请求某个Bean时
                    │
                    ├── 1. 实例化Bean(通常通过无参构造函数)
                    │
                    ├── 2. 依赖解析与注入
                    │       │
                    │       ├── 2.1 依赖发现(分析构造器/方法/字段)
                    │       │
                    │       ├── 2.2 依赖查找(在容器中查找匹配Bean)
                    │       │       │
                    │       │       └── 考虑类型、限定符、@Primary等
                    │       │
                    │       └── 2.3 依赖注入(通过反射设置依赖)
                    │               │
                    │               ├── 构造器注入(通过构造函数)
                    │               ├── Setter注入(通过setter方法)
                    │               └── 字段注入(直接设置字段值)
                    │
                    ├── 3. 初始化Bean
                    │       │
                    │       ├── 调用@PostConstruct方法
                    │       ├── 调用InitializingBean.afterPropertiesSet()
                    │       └── 调用配置的init-method方法
                    │
                    └── 4. Bean就绪可用(可被应用代码使用)

四、DI过程的关键组件

1. BeanFactory

Spring容器的核心接口,负责Bean的创建、配置和管理。ApplicationContext是BeanFactory的高级扩展。

2. BeanDefinition

包含Bean的元数据信息,如类名、作用域、构造函数参数、属性值等。Spring根据BeanDefinition创建实际的Bean实例。

3. BeanPostProcessor

Bean后处理器接口,允许在Bean初始化前后进行自定义处理,是实现AOP、属性编辑器等功能的基础。

4. AutowiredAnnotationBeanPostProcessor

专门处理@Autowired@Value注解的后处理器,是实现自动注入的关键组件。

五、DI过程优化与扩展

1. 延迟初始化

通过@Lazy注解或配置,可以让Bean在首次使用时才创建,提高启动性能。

2. 条件化Bean创建

通过@Conditional注解,可以根据条件决定是否创建某个Bean。

3. Bean作用域管理

除了单例(Singleton)外,还可以配置原型(Prototype)、请求(Request)、会话(Session)等不同作用域的Bean。

4. Profile-specific Bean

通过@Profile注解,可以为不同环境(如dev、test、prod)配置不同的Bean实现。

六、最佳实践

  1. 优先使用构造器注入​:特别是对于强制依赖,构造器注入更明确且利于不可变对象创建
  2. 合理使用Setter注入​:对于可选依赖,Setter注入更灵活
  3. 避免字段注入​:虽然方便,但降低了代码的可测试性和明确性
  4. 明确依赖关系​:使用@Qualifier解决多实现类的歧义问题
  5. 利用Spring Boot自动配置​:在Spring Boot中,大量使用约定优于配置的原则简化DI配置

通过理解Spring DI的完整过程,开发者可以更好地利用Spring框架的强大功能,编写更清晰、更可维护的应用程序代码。

© 版权声明
THE END
喜欢就点个赞,支持一下吧!
点赞70 分享
评论 抢沙发
头像
欢迎您留下评论!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容