关于Spring的两三事:夭寿了,官方给出的依赖注入方式只有两种?!
人生苦短,不如养狗
作者:闲宇
公众号:Brucebat的伪技术鱼塘
一、前言
当我们在使用Spring框架进行Bean依赖关系管理时一般会使用如下三种方式:
基于构造器的注入方式
基于setter方法的注入方式
基于注解(诸如
@Autowired
、@Resource
等注解)的方式
这三种方式可以说是我们日常开发过程使用最多的三种方式,以至于在我们搜索“Spring依赖注入”时绝大部分博客都会给出这三种方式。但前段时间闲宇在探究Spring三级缓存时,在翻阅官方文档中的过程无意中发现在诸多面试宝典中作为标准答案的“Spring三种依赖方式”竟然是错误的。那么下面我们就来结合官方文档具体分析一下日常使用的这三种依赖管理方式到底是什么。
本文更多是的结合官方文档对网络上现存的一些可能有问题的概念进行勘误分析,如果想跳过这些分析的朋友可以直接阅读官方文档:https://docs.spring.io/spring-framework/reference/core/beans/dependencies/factory-collaborators.html
二、什么是依赖注入
1. 基本概念
Dependency injection (DI) is a process whereby objects define their dependencies (that is, the other objects with which they work) only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean. This process is fundamentally the inverse (hence the name, Inversion of Control) of the bean itself controlling the instantiation or location of its dependencies on its own by using direct construction of classes or the Service Locator pattern.
这是一段来自官方文档给出的关于依赖注入(Dependency Injection,即DI)的解释。这段话给我们传递了两层含义:
对象通过构造函数的参数、工厂方法的参数以及setter方法的参数来定义它们依赖的其他对象;
实际的依赖填充过程是由外部容器而不是对象自己完成(这一过程也因此得名为控制反转,即IC);
2. 官方给出的依赖注入方式只有两种?!
是的,当闲宇看到官方文档的给出的如下内容时我也震惊了:
从官方文档当中我们可以得知,依赖注入只存在于以下两种主要变体中:
基于构造器的依赖注入;
基于setter方法的依赖注入;
好吧,所以背了那么多年的八股文竟然是错的,想想就有点震碎我的三观。但是在了解了这两种方式的使用流程和具体源码之后,其实也不难发现确实如同定义中给出的一样开发者只需要定义Bean之间的依赖关系,具体依赖关系的填充处理交由Spring容器(其实我更喜欢使用上下文来称呼容器,不过我们还是按照官方的来)来处理。需要注意的是,在原始的方案当中(即不使用任何注解的情况)需要通过XML配置文件来描述Bean之间的关系。Spring框架用于处理这两种方式的源码方法如下:
基于构造器的依赖注入方法:
AbstractAutowireCapableBeanFactory#createBeanInstance
;基于setter方法的依赖注入方法:
AbstractAutowireCapableBeanFactory#applyPropertyValues
;
具体的源码大家可以自行阅读,建议在研究源码的过程当中最好使用一个实际的案例来debug一下。
三、什么是自动装配
既然官方给出的依赖注入方式只有两种,那么我们日常使用的基于注解的方式又算什么呢?在解开这个问题之前我们需要先了解一下自动装配的概念。
The Spring container can autowire relationships between collaborating beans. You can let Spring resolve collaborators (other beans) automatically for your bean by inspecting the contents of the
ApplicationContext
.
从官方的介绍当中我们可以了解到自动装配实际上就是Spring容器提供的一种自动检索并填充所依赖Bean的能力。通过这种能力,开发者可以不需要在XML配置文件当中明确所依赖Bean的具体信息,甚至可以不再需要通过XML配置文件的方式手动定义Bean之间的依赖关系。从编码角度来看,开发者可以更为便捷地进行Bean依赖关系的处理,省却了不少繁文缛节的时间。为了更好地让开发者使用这一能力,Spring提供了两种方式:
基于XML配置文件的自动装配
基于注解的自动装配(诸如
@Autowired
、@Resource
、@Value
、@Primary
注解)
随着SpringBoot的流行,前者已经很少看到使用,而后者则被广泛运用于我们日常的开发当中。
其实当笔者在仔细研究了Spring框架基于注解注入方式的源码之后发现了一个有趣的事情,就是当我们使用了@Autowired
或者@Resource
注解来标记某个方法(非构造器)时,只要方法体内的逻辑和setter方法保持一致,其实不需要一定命名成setter方法也可以实现注入逻辑。也就是说,方法名称的约束其实是不存在的,我们完全可以不参照官方给出的示例,自己定义一个方法来进行基于注解的注入处理。从这个角度来看,基于注解的方式其实也让我们的编码变得更加灵活了。
基于注解的注入处理逻辑可以参看Spring源码中的
CommonAnnotationBeanPostProcessor#postProcessProperties
和AutowiredAnnotationBeanPostProcessor#postProcessProperties
两个方法。
四、总结
从上面的分析可以看到,面试当中所问的Spring依赖注入方式应该只有两种,基于注解的方式实际上Spring框架提供的简化依赖注入功能(即自动装配)的其中一种方式。这些方式都可以归类为Spring提供给开发者的依赖描述和管理方法。所以官方文档才是学习一个框架或者工具第一手资料,至于为什么不是源码,因为源码当中的一些设计你要是不让当初开发这段代码的大佬过来解释一下你也搞不清楚他是怎么想的。
最后,祝大家身体健康,心想事成,早日财富自由~~