1.IoC(Inversion of Control) 控制反转,即“不用打电话过来,咱们会打给你”。 两种实现: 依赖查找(DL)和依赖注入(DI)。面试
IOC 和 DI 、DL 的关系(这个 DL,Avalon 和 EJB 就是使用的这种方式实现的 IoC): 数据库
3.DI 是 Spring 使用的方式,容器负责组件的装配。工具
注意:Java 使用 DI 方式实现 IoC 的不止 Spring,包括 Google 的 Guice,还有一个冷门的 PicoContainer(极度轻量,但只提供 IoC)。ui
Spring 的 IoC 设计支持如下功能:设计
支持回调某些方法(可是须要实现 Spring 接口,略有侵入) 其中,最重要的就是依赖注入,从 XML 的配置上说, 即 ref 标签。对应 Spring RuntimeBeanReference 对象。cdn
对于 IoC 来讲,最重要的就是容器。容器管理着 Bean 的生命周期,控制着 Bean 的依赖注入。 那么, Spring 如何设计容器的呢?server
Spring 做者 Rod Johnson 设计了两个接口用以表示容器。对象
ApplicationContext 能够称之为 “高级容器”。由于他比 BeanFactory 多了更多的功能。他继承了多个接口。所以具有了更多的功能。blog
例如资源的获取,支持多种消息(例如 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 的不断升级,功能愈来愈多,不少人在这里迷失了方向 :)。
简单说就是:
因此,必定要把 “低级容器” 和“高级容器” 的区别弄清楚。不能一叶障目不见泰山。
好,当咱们建立好容器,就会使用 getBean 方法,获取 Bean,而 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 个步骤:
能够看到,依赖注入实际上,只须要 “低级容器” 就能够实现。
这就是 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 初始化过程,就不再会含糊其词、支支吾吾了!!!