原理:依赖反转模式
合作对象的引用或依赖关系的管理交由容器或平台来完成,而不是具体的对象。
IOC容器接口体系
- 实现BeanFactory接口的简单容器系列:实现了容器的最基本的功能
- ApplicationContext应用上下文:容器的高级特性
BeanFactory容器设计原理(以XmlBeanFactory实现为例)
public class XmlBeanFactory extends DefaultListableBeanFactory {
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
}
- XmlBeanFactory继承DefaultListableBeanFactory容器(默认功能完整的IOC容器)
- 通过XmlBeanDefinitionReader添加新功能
编程式使用IOC容器
注意观察IOC容器实现中的那些关键的类(Resource,DefaultListableBeanFactory和 BeanDenifitionReader)之间的相互关系。如何把IOC容器功能解耦,又如何结合在一起为IOC容器服务。以下代码都有注释:
/**
* Created by 阿越.
* 以编程的方式使用DefaultListableBeanFactory
*/
public void testSpringIOC(){
// 创建IOC配置文件抽象资源(含有BeanDefinition)
ClassPathResource resource = new ClassPathResource("beans.xml");
// 创建一个BeanFactory
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// 载入BeanDenifition的读取器
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
// 读入配置信息
reader.loadBeanDefinitions(resource);
}
ApplicationContext容器设计原理
(以FileSystemXmlApplicationContext实现为例)
// 实例化这个应用上下文支持,启动IOC容器的refresh()
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
// 从文件系统中加载XML的Bean定义资源
@Override
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
- 与BeanFactory容器的设计原理相似,先以继承或构造方法的形式实现父类功能,在此基础上,添加自己的新功能。
IOC容器的初始化过程
- Resource定位:BeanDefinition的资源定位
- BeanDefinition的载入:把用户定义好的Bean表示成IOC容器内部数据结构BeanDefinition
- 向IOC容器注册BeanDefinition:把BeanDefinition注入到IOC内部的一个HashMap中
Bean定义的载入和依赖注入是两个独立的过程
- Bean定义(BeanDefinition)的载入是在IOC容器初始化时完成
- 依赖注入默认是在开发者第一次通过getBean向容器索取Bean的时完成
- 设置lazyinit属性,使该Bean的依赖注入在IOC容器初始化时就预先完成
refresh():IOC容器初始化入口,载入BeanDefinition的入口
以FileSystemXmlApplicationContext为例
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
// IOC容器初始化入口
refresh();
}
}
之后过程图解如下(感兴趣的可以照着图看源码):
BeanDefinition在IOC容器中的注册
在DefaultListableBeanFactory实现BeanDefinitionRegistry接口,这个接口的实现完成BeanDefinition向容器的注册。具体实现可看源码。
IOC容器的依赖注入
getBean接口的实现触发依赖注入,是起点,内部用doGetBean()实现,之后会调用createBean。图解如下:
参考资料:Spring技术内幕(第2版) 计文柯