IoC控制反转
参考的原文
两种实现方式:DL(依赖查找)和DI(依赖注入)
主要用的还是DI
DI是spring使用的方式,容器负责组建的装配。
Java 使用 DI 方式实现 IoC 的不止 Spring,包括 Google 的 Guice,还有一个冷门的 PicoContainer(极度轻量,但只提供 IoC。
SpringIoC设计支持以下功能
- 依赖注入
- 依赖检查
- 自动装配
- 支持集合
- 指定初始化方法和销毁方法
- 支持回调某些方法(但是需要实现spring接口,略有入侵)
最重要的就是依赖注入,从XML的配置上说,即ref标签。对应Spring RuntimeBeanReference对象
IoC中最重要的是容器,容器管理着Bean的生命周期,控制Bean的控制注入
容器的设计
两个接口
- BeanFactory
- ApplicationContext
BeanFactory可以理解成HashMap,Key是BeanName,Value是Bean实例,通常只提供注册(put)和获取(get)两个功能,称之为低级容器
ApplicationContext称之为高级容器,应用上下文,该接口定义了一个 refresh 方法,此方法是所有阅读 Spring 源码的人的最熟悉的方法,用于刷新整个容器,即重新加载/刷新所有的 bean
通过常用ClassPathXmlApplicationContext类,来展示整个容器的层级 UML 关系。
Spring IoC的初始化过程
- 用户构造
- CPAC首先访问抽象高级容器的final的refresh方法,这个方法是模板方法,所以要回调子类(低级容器)的refreshBeanFactory方法,使用低级容器加载所有BeanDefinition和properties到容器中
3.低级容器加载成功后,高级容器开始处理一些回调,例如 Bean 后置处理器。回调 setBeanFactory 方法。或者注册监听器等,发布事件,实例化单例 Bean 等等功能,这些功能,随着 Spring 的不断升级,功能越来越多
简单的说法:低级容器 加载配置文件(从 XML,数据库,Applet),并解析成 BeanDefinition 到低级容器中。
加载成功后,高级容器启动高级功能,例如接口回调,监听器,自动实例化单例,发布事件等等功能。
创建好容器后就会使用getBean方法,获取Bean和getBean流程图:
递归操作的原因:当 Bean_A 依赖着 Bean_B,而这个 Bean_A 在加载的时候,其配置的 ref = “Bean_B” 在解析的时候只是一个占位符,被放入了 Bean_A 的属性集合中,当调用 getBean 时,需要真正 Bean_B 注入到 Bean_A 内部时,就需要从容器中获取这个 Bean_B,因此产生了递归。加载的顺序不同所以不能直接注入,采用递归的方法。
spring将其分为2个步骤
1.加载所有的Bean配置成BeanDefinition到容器中,如果Bean有依赖关系,则使用占位符暂时替代
2.调用getBean的时候,进行真正的依赖注入,即如果碰到了属性是 ref 的(占位符),那么就从容器里获取这个 Bean,然后注入到实例中 —— 称之为依赖注入。
依赖注入实际上,只需要 “低级容器” 就可以实现。
总结
IoC 在 Spring 里,只需要低级容器就可以实现,2 个步骤:
a. 加载配置文件,解析成 BeanDefinition 放在 Map 里。
b. 调用 getBean 的时候,从 BeanDefinition 所属的 Map 里,拿出 Class 对象进行实例化,同时,如果有依赖关系,将递归调用 getBean 方法 —— 完成依赖注入。
上面就是 Spring 低级容器(BeanFactory)的 IoC。
至于高级容器 ApplicationContext,他包含了低级容器的功能,当他执行 refresh 模板方法的时候,将刷新整个容器的 Bean。同时其作为高级容器,包含了太多的功能。一句话,他不仅仅是 IoC。他支持不同信息源头,支持 BeanFactory 工具类,支持层级容器,支持访问文件资源,支持事件发布通知,支持接口回调等等。