本节书摘来自华章出版社《SpringBoot揭秘:快速构建微服务体系》一书中的第2章,第2.2节Spring IoC其实很简单,作者王福强,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
2.2 Spring IoC其实很简单
有部分Java开发者对IoC(Inversion Of Control)和DI(Dependency Injection)的概念有些混淆,认为二者是对等的,实际上我在之前的著作中已经说过了,IoC其实有两种方式,一种就是DI,而另一种是DL,即Dependency Lookup(依赖查找),前者是当前软件实体被动接受其依赖的其他组件被IoC容器注入,而后者则是当前软件实体主动去某个服务注册地查找其依赖的那些服务,概念之间的关系如图2-1所示可能更贴切些。
我们通常提到的Spring IoC,实际上是指Spring框架提供的IoC容器实现(IoC Container),而使用Spring IoC容器的一个典型代码片段就是:
public class App {
public static void main(String[] args) {
ApplicationContext context = new FileSystemXmlApplication-Context("...");
// ...
MockService service = context.getBean(MockService.class);
service.doSomething();
}
}
任何一个使用Spring框架构建的独立的Java应用(Standalone Java Application),通常都会存在一行类似于“context.getBean(..);”的代码,实际上,这行代码做的就是DL的工作,而构建的任何一种IoC容器背后(比如BeanFactory或者ApplicationContext)发生的事情,则更多是DI的过程(也可能有部分DL的逻辑用于对接遗留系统)。
Spring的IoC容器中发生的事情其实也很简单,总结下来即两个阶段:
(1)采摘和收集“咖啡豆”(bean)
(2)研磨和烹饪咖啡
哦,不对,这是一本技术书,差点儿写成咖啡文化杂志。
那我们还是回过头来继续说Spring IoC容器的依赖注入流程吧!Spring IoC容器的依赖注入工作可以分为两个阶段:
阶段一:收集和注册
第一个阶段可以认为是构建和收集bean定义的阶段,在这个阶段中,我们可以通过XML或者Java代码的方式定义一些bean,然后通过手动组装或者让容器基于某些机制自动扫描的形式,将这些bean定义收集到IoC容器中。
假设我们以XML配置的形式来收集并注册单一bean,一般形式如下:
...
如果嫌逐个收集bean定义麻烦,想批量地收集并注册到IoC容器中,我们也可以通过XML Schema形式的配置进行批量扫描并采集和注册:
基于JavaConfig形式的收集和注册,不管是单一还是批量,后面我们都会单独提及。
阶段二:分析和组装
当第一阶段工作完成后,我们可以先暂且认为IoC容器中充斥着一个个独立的bean,它们之间没有任何关系。但实际上,它们之间是有依赖关系的,所以,IoC容器在第二阶段要干的事情就是分析这些已经在IoC容器之中的bean,然后根据它们之间的依赖关系先后组装它们。如果IoC容器发现某个bean依赖另一个bean,它就会将这另一个bean注入给依赖它的那个bean,直到所有bean的依赖都注入完成,所有bean都“整装待发”,整个IoC容器的工作即算完成。
至于分析和组装的依据,Spring框架最早是通过XML配置文件的形式来描述bean与bean之间的关系的,随着Java业界研发技术和理念的转变,基于Java代码和Annotation元信息的描述方式也日渐兴盛(比如@Autowired和@Inject),但不管使用哪种方式,都只是为了简化绑定逻辑描述的各种“表象”, 最终都是为本阶段的最终目的服务。
很多Java开发者一定认为spring的XML配置文件是一种配置(Configuration),但本质上,这些配置文件更应该是一种代码形式,XML在这里其实可以看作一种DSL,它用来表述的是bean与bean之间的依赖绑定关系,诸君还记得没有IoC容器的年代要自己写代码新建(new)对象并配置(set)依赖的吧?