Spring IOC流程清楚不?聊聊看

Spring IOC流程清楚不?聊聊看

作者:Smartの明

http://yeming.me/2016/05/13/threadPool2/

IOC简要解释

在应用开发中,开发人员往往需要引用和调用其它组件的服务,这种依赖关系如果固化在组件设计中,就会造成依赖关系的僵化和以后维护成本的增加。如果使用IoC容易,把资源的获取反转,具体相对java来说就是把bean的依赖关系交给IoC容易来处理。在反转实现中,具体通过可读的文件来配置。并且bean的耦合关系需要变动时候,可以不用重新改变或者编译源代码就可以实现,这也符合设计原则中的开闭原则,能够提供组件系统设计的灵活性。基于此,我们下面来简要分析一下Spring IoC相关的源码

ApplicationContext

Spring IOC流程清楚不?聊聊看通过上面的类图,可以看到ApplicationContext是基于BeanFactory接口扩展来的,但它还继承了一些其它的接口,所以它是一个Spring提供给我们的一个更高级的IoC容易。下面我们来分析一下ApplicationContext启动部分的源码。我们看到Spring IOC流程清楚不?聊聊看

  1. public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)

  2. throws BeansException {

  3. super(parent);

  4. setConfigLocations(configLocations);

  5. if (refresh) {

  6. refresh();

  7. }

  8. }

基于上面的ClassPathXmlApplicationContext可以看到它会调用AbstractApplicationContext的refresh方法,这个也是IoC启动的核心代码,下面就来看下。我们可以看到refresh方法里面又包含很多步骤,我们主要关注一下红线标记出来的那个方法,它就是Spring IoC的初始化。

refresh的时序图

Spring IOC流程清楚不?聊聊看

通过上面的时序图,我们可以看到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中的每一个获取到的标签 Spring IOC流程清楚不?聊聊看

  • 最终通过BeanDefinitionParserDelegate..parseBeanDefinitionElement(Element)来解析Element,并用BeanDefinitionHolder来持有BeanDifinition。 Spring IOC流程清楚不?聊聊看

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中的

  1. // Instantiate all remaining (non-lazy-init) singletons.

  2. finishBeanFactoryInitialization(beanFactory);

上述方法会调用到getBean(从上面代码的注释可以看出它只能实例化没有设置lazy-init的bean,如果设置了lazy-init只能在最终applicationContext.getbean的时候完成依赖注入)这个getbean会通过判断当前bean是否有depends-on的bean,若果有,则对依赖的depend-on的bean在递归调用get Bean。最终完成bean的依赖注入。

END

Java面试题专栏

【51期】一道阿里面试题:说说你知道的关于BeanFactory和FactoryBean的区别
【52期】记一道简单的Java面试题,但答错率很高!
【53期】面试官:谈一下数据库分库分表之后,你是如何解决事务问题?
【54期】Java序列化三连问,是什么?为什么需要?如何实现?
【55期】面试中经常被问到Java引用类型原理,带你深入剖析
【56期】你说你熟悉并发编程,那么你说说Java锁有哪些种类,以及区别
【57期】面试官问,MySQL建索引需要遵循哪些原则呢?
【58期】盘点那些面试中最常问的MySQL问题,第一弹!
【59期】MySQL索引是如何提高查询效率的呢?(MySQL面试第二弹)
【60期】事务隔离级别中的可重复读能防幻读吗?(MySQL面试第三弹)


Spring IOC流程清楚不?聊聊看


欢迎长按下图关注公众号后端技术精选

Spring IOC流程清楚不?聊聊看

原文始发于微信公众号(后端技术精选):Spring IOC流程清楚不?聊聊看