IOC简要解释
在应用开发中,开发人员往往需要引用和调用其它组件的服务,这种依赖关系如果固化在组件设计中,就会造成依赖关系的僵化和以后维护成本的增加。如果使用IoC容易,把资源的获取反转,具体相对java来说就是把bean的依赖关系交给IoC容易来处理。在反转实现中,具体通过可读的文件来配置。并且bean的耦合关系需要变动时候,可以不用重新改变或者编译源代码就可以实现,这也符合设计原则中的开闭原则,能够提供组件系统设计的灵活性。基于此,我们下面来简要分析一下Spring IoC相关的源码
ApplicationContext
通过上面的类图,可以看到ApplicationContext是基于BeanFactory接口扩展来的,但它还继承了一些其它的接口,所以它是一个Spring提供给我们的一个更高级的IoC容易。
下面我们来分析一下ApplicationContext启动部分的源码。我们看到
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
基于上面的ClassPathXmlApplicationContext可以看到它会调用AbstractApplicationContext的refresh方法,这个也是IoC启动的核心代码,下面就来看下。
我们可以看到refresh方法里面又包含很多步骤,我们主要关注一下红线标记出来的那个方法,它就是Spring IoC的初始化。
refresh的时序图
通过上面的时序图,我们可以看到ioc启动大致可以分为3个过程。
- resource的定位过程,可以简单理解就是找到我们定义bean的相关的xml文件
- BeanDifinition的载入,就是把用户在xml中定义好的bean解析成为IoC的内部数据结构,也就是BeanDifinition。
- 向IoC容易注册这些BeanDifinition,把BeanDifinition注册到一个Map中保存。
下面我们以上面的时序图为基础,分别对上面的3个过程简要分析一下。
resource的定位
- ClassPathXmlApplicationContext的构造方法setConfigLocations(configLocations);这个最终设置了AbstractRefreshableConfigApplicationContext的configLocations属性
- AbstractXmlApplicationContext.loadBeanDefinitions()方法会调用到XmlBeanDefinitionReader.loadBeanDefinitions(configLocations);
- XmlBeanDefinitionReader.doLoadBeanDefinitions(EncodedResource encodedResource)这就把一开始传入的一个指向xml的路径变成了一个程序可以读的encodedResource,就完成了resource的定位。
BeanDifinition的载入和解析
- XmlBeanDefinitionReader.doLoadBeanDefinitions(InputSource inputSource, Resource resource)这个方法把资源封装成一个Document
- DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)这个方法解析document中的每一个获取到的标签
- 最终通过BeanDefinitionParserDelegate..parseBeanDefinitionElement(Element)来解析Element,并用BeanDefinitionHolder来持有BeanDifinition。
BeanDifinition在IoC容器中的注册
- BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());其中这个getReaderContext().getRegistry()返回的就是最早创建的DefaultListableBeanFactory
- DefaultListableBeanFactory.registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
- DefaultListableBeanFactory持有下面这个对象
private final Map beanDefinitionMap = new ConcurrentHashMap(256);每次基本都是putIfAbsent模式以放到beanDefinitionMap,这样就完成了bean的注册过程。
IoC容器的依赖注入
上面3个过程只是完成了每个单独在xml中定义的bean在IoC容器中的注册,但是我们知道很多bean最终的实例化都要依赖其它bean。然而这个依赖注入我们从上面的代码来看,并没有完成这个过程。事实上,依赖注入是在refresh中的
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
上述方法会调用到getBean(从上面代码的注释可以看出它只能实例化没有设置lazy-init的bean,如果设置了lazy-init只能在最终applicationContext.getbean的时候完成依赖注入)这个getbean会通过判断当前bean是否有depends-on的bean,若果有,则对依赖的depend-on的bean在递归调用get Bean。最终完成bean的依赖注入。