IOC 和 DI 、DL 的关系(这个 DL,Avalon 和 EJB 便是利用的这种办法实现的 IoC):

DL 已经被抛弃,由于他须要用户自己去是利用 API 进行查找资源和组装工具。
即有侵入性。
DI 是 Spring 利用的办法,容器卖力组件的装置。

把稳:Java 利用 DI 办法实现 IoC 的不止 Spring,包括 Google 的 Guice,还有一个冷门的 PicoContainer(极度轻量,但只供应 IoC)。

Spring 的 IoC

父类jsp页面调子类的方法面试必问的 Spring IOC要看看了 Docker

Spring 的 IoC 设计支持以下功能:

依赖注入依赖检讨自动装置支持凑集指定初始化方法和销毁方法支持回调某些方法(但是须要实现 Spring 接口,略有侵入)

个中,最主要的便是依赖注入,从 XML 的配置上说, 即 ref 标签。
对应 Spring RuntimeBeanReference 工具。

对付 IoC 来说,最主要的便是容器。
容器管理着 Bean 的生命周期,掌握着 Bean 的依赖注入。

那么, Spring 如何设计容器的呢?

Spring 作者 Rod Johnson 设计了两个接口用以表示容器。

BeanFactoryApplicationContext

BeanFactory 粗暴大略,可以理解为便是个 HashMap,Key 是 BeanName,Value 是 Bean 实例。
常日只供应注册(put),获取(get)这两个功能。
我们可以称之为 “低级容器”。

ApplicationContext 可以称之为 “高等容器”。
由于他比 BeanFactory 多了更多的功能。
他继续了多个接口。
因此具备了更多的功能。

例如资源的获取,支持多种(例如 JSP tag 的支持),对 BeanFactory 多了工具级别的支持等待。
以是你看他的名字,已经不是 BeanFactory 之类的工厂了,而是 “运用高下文”, 代表着全体大容器的所有功能。

该接口定义了一个 refresh 方法,此方法是所有阅读 Spring 源码的人的最熟习的方法,用于刷新全体容器,即重新加载/刷新所有的 bean。

当然,除了这两个大接口,还有其他的赞助接口,但我本日不会花太多篇幅先容他们。

为了更直不雅观的展示 “低级容器” 和 “高等容器” 的关系,我这里通过常用的 ClassPathXmlApplicationContext 类,来展示全体容器的层级 UML 关系。

有点繁芜?先不要慌,我来阐明一下。

最上面的 BeanFactory 知道吧?我就不讲了。

下面的 3 个绿色的,都是功能扩展接口,这里就不展开讲。

看下面的从属 ApplicationContext 粉赤色的 “高等容器”,依赖着 “低级容器”,这里说的是依赖,不是继续哦。
他依赖着 “低级容器” 的 getBean 功能。
而高等容器有更多的功能:支持不同的信息源头,可以访问文件资源,支持运用事宜(Observer 模式)。

常日用户看到的便是 “高等容器”。
但 BeanFactory 也非常够用啦!

左边灰色区域的是 “低级容器”, 只负载加载 Bean,获取 Bean。
容器其他的高等功能是没有的。
例如上图画的 refresh 刷新 Bean 工厂所有配置。
生命周期事宜回调等。

好,阐明了低级容器和高等容器,我们可以看看一个 IoC 启动过程是什么样子的。
说白了,便是 ClassPathXmlApplicationContext 这个类,在启动时,都做了啥。
(由于我这是 interface21 的代码,肯定和你的 Spring 4.x 系列不同)。

下图是 ClassPathXmlApplicationContext 的布局过程,实际便是 Spring IoC 的初始化过程。

把稳,这里为了理解方便,有所简化。

这里再用笔墨来描述这个过程:

用户布局 ClassPathXmlApplicationContext(简称 CPAC)CPAC 首先访问了 “抽象高等容器” 的 final 的 refresh 方法,这个方法是模板方法。
以是要回调子类(低级容器)的 refreshBeanFactory 方法,这个方法的浸染是利用低级容器加载所有 BeanDefinition 和 Properties 到容器中。
低级容器加载成功后,高等容器开始处理一些回调,例如 Bean 后置处理器。
回调 setBeanFactory 方法。
或者注册监听器等,发布事宜,实例化单例 Bean 等等功能,这些功能,随着 Spring 的不断升级,功能越来越多,很多人在这里迷失落了方向 :)。

大略说便是:

低级容器 加载配置文件(从 XML,数据库,Applet),并解析成 BeanDefinition 到低级容器中。
加载成功后,高等容器启动高等功能,例如接口回调,监听器,自动实例化单例,发布事宜等等功能。

以是,一定要把 “低级容器” 和“高等容器” 的差异弄清楚。
不能一叶障目不见泰山。

好,当我们创建好容器,就会利用 getBean 方法,获取 Bean,而 getBean 的流程如下:

从图中可以看出,getBean 的操作都是在低级容器里操作的。
个中有个递归操作,这个是什么意思呢?

假设:当 Bean_A 依赖着 Bean_B,而这个 Bean_A 在加载的时候,其配置的 ref = “Bean_B” 在解析的时候只是一个占位符,被放入了 Bean_A 的属性凑集中,当调用 getBean 时,须要真正 Bean_B 注入到 Bean_A 内部时,就须要从容器中获取这个 Bean_B,因此产生了递归。

为什么不是在加载的时候,就直接注入呢?由于加载的顺序不同,很可能 Bean_A 依赖的 Bean_B 还没有加载好,也就无法从容器中获取,你不能哀求用户把 Bean 的加载顺序排列好,这是不人性的。

以是,Spring 将其分为了 2 个步骤:

加载所有的 Bean 配置成 BeanDefinition 到容器中,如果 Bean 有依赖关系,则利用占位符暂时代替。
然后,在调用 getBean 的时候,进行真正的依赖注入,即如果碰到了属性是 ref 的(占位符),那么就从容器里获取这个 Bean,然后注入到实例中 —— 称之为依赖注入。
可以看到,依赖注入实际上,只须要 “低级容器” 就可以实现。

这便是 IoC。

以是 ApplicationContext refresh 方法里面的操作不但是 IoC,是高等容器的所有功能(包括 IoC),IoC 的功能在低级容器里就可以实现。

总结

说了这么多,不知道你有没有理解Spring IoC?这里小结一下:IoC 在 Spring 里,只须要低级容器就可以实现,2 个步骤:

加载配置文件,解析成 BeanDefinition 放在 Map 里。
调用 getBean 的时候,从 BeanDefinition 所属的 Map 里,拿出 Class 工具进行实例化,同时,如果有依赖关系,将递归调用 getBean 方法 —— 完成依赖注入。

上面便是 Spring 低级容器(BeanFactory)的 IoC。

至于高等容器 ApplicationContext,他包含了低级容器的功能,当他实行 refresh 模板方法的时候,将刷新全体容器的 Bean。
同时其作为高等容器,包含了太多的功能。
一句话,他不仅仅是 IoC。
他支持不同信息源头,支持 BeanFactory 工具类,支持层级容器,支持访问文件资源,支持事宜发布关照,支持接口回调等等。

可以预见,随着 Spring 的不断发展,高等容器的功能会越来越多。

诚然,理解 IoC 的过程,实际上为了理解 Spring 初始化时,各个接口的回调机遇。
例如 InitializingBean,BeanFactoryAware,ApplicationListener 等等接口,这些接口的浸染,笔者之前写过一篇文章进行先容,有兴趣可以看一下,关键字:Spring 必知必会 扩展接口。

但是请把稳,实现 Spring 接口代表着你这个运用就绑定去世 Spring 了!
代表 Spring 具有侵入性!
要知道,Spring 发布时,无侵入性便是他最大的宣扬点之一 —— 即 IoC 容器可以随便改换,代码无需变动。
而现如今,Spring 已然成为 J2EE 社区准官方办理方案,也没有了所谓的侵入性这个说法。
由于他便是标准,和 Servlet 一样,你能不实现 Servlet 的接口吗?: -)

好了,下次如果再有口试官问 Spring IoC 初始化过程,就再也不会暗昧其词、支支吾吾了!


原文:【口试】必问的 Spring IOC,要看看了!


作者:莫那·鲁道

来源:微信"大众年夜众号,Java研发军团

声明:本文为二次转载,原文不可查