spring ioc工作原理剖析梗概(version-4.03)

        spring ioc容器作为控制反转(也称依赖注入)原则的实现,它通过抽象对象间的依赖关系成外部的数据(BeanDefinition),将具体对象间的依赖关系从代码层抽离出来,使得对象间仅仅保留被依赖对象的接口,这样一来接口与具体实现就解耦开了。在初始化对象的时候,ioc容器读取这些抽象到外部的依赖数据并实例化,然后将实例化的依赖注入到对象里面去,让对象处于就绪状态,这就是ioc的工作过程。所以spring ioc容器总体上分为两个步骤,第一步是处理依赖元数据——容器初始化,第二步是依赖注入。

        首先来介绍第一步容器初始化,初始化步骤包括读取依赖元数据、解析依赖元数据和向ioc容器注册元数据。读取依赖元数据首先就要知道元数据的位置与元数据存放载体,元数据一般有两种载体——xml配置文件和annotation(注解),注解(元数据)是在一种在代码中添加信息形式化方法,这些信息是有关程序的额外信息,不参与程序内部的计算(非成员方法输入),需要编写注解处理器来读取注解存储的信息。在web环境下,xml配置文件是在web.xml最前面文件中配置<context-param>节点来指定的,param-name为contextConfigLocation(默认配置参数名),然后配置org.springframework.web.context.ContextLoaderListener,这个ContextLoadListener会监听servlet容器的初始化和销毁servletContext事件,servlet容器初始化一个webapp的ContextLoaderListener就会进入context初始化入口contextInitialized,里面就会为当前context创建XmlWebApplicationContext实例,然后将配置在web.xml的参数contextConfigLocation设置到XmlWebApplicationContext的configLocations。这样就得到元数据所在位置了。接下来调用ioc容器初始化入口XmlWebApplicationContext的refresh方法。容器初始化的第一步就是创建BeanFactory实例,然后调用loadBeanDefinitions加载configLocations配置文件中元数据,在加载之前创建XmlBeanDefinitionReader实例,调用XmlBeanDefinitionReader(简称beanDefinitionReader)实例的loadBeanDefinitions方法加载、解析各种元数据、注册BeanDefinition到ioc容器。beanDefinitionReaderloadBeanDefinitions实现中,调用XmlWebApplicationContext(实现了ResourceLoader接口)实例的getResources方法获取资源的输入流,至此加载步骤完成。然后进入解析步骤,首先将流转换成Document实例,然后创建BeanDefinitionDocumentReader实例,BeanDefinitionDocumentReader根据springbeans XSD解析bean定义。解析的时候namespacee分为两种,一种是默认namespace,一种是自定义namespace,Document结点都会有namespaceUri的,默认的namespaceUri是http://www.springframework.org/schema/beans,其余属于自定义namespace。对于自定义namespace,使用BeanDefinitionParserDelegate进行解析,不同的自定义namespace根据namespaceUri相应的NamespaceHandler实例处理,例如ContextNamespaceHandler、MvcNamespaceHandler等等;而默认namespace解析处理节点name为import、alias、bean和beans。import节点定义新的配置文件,alias是用来定义别名的,bean节点是容器管理的对象定义,BeanDefinitionParserDelegate将bean节点转换成BeanDefinitionHolder注册到beanFacotry。至此解析和注册也完成了。

        ioc容器初始化完成后,依赖抽象BeanDefinition已经注册到ioc容器,接下来是要进行bean的实例化和依赖注入。不过在进行依赖注入前,还有一些工作是需要进行处理的,配置ioc容器标准特性,实例化并调用BeanFactoryPostProcessor(用于与BeanDefinition交互或修改),将bean的postProcessor注册到容器上面去、,初始化国际化源,初始化ApplicationEventMulticaster(广播ioc容器事件给listener),初始化theme(用于spring mvc中的风格资源),注册ApplicationListener到ApplicationEventMulticaster。然后就是调用beanFactory根据注册的BeanDefinition预实例化Singleton的bean和依赖注入了,实例化bean和依赖注入的入口是getBean,getBean是贪婪的,也就是假设bean已经实例化并注入依赖的(就绪状态),首先从容器获取,不存在就实例化。实例化的过程中先获取BeanDefinition,检查该bean的间接依赖(dependsOn),如果有间接依赖,那么先实例化间接依赖。调用beanPostProcessor进行处理,然后使用反射进行实例化,对于没有参数的使用Class.newInstance()实例化,对于有参构造使用Class.getDeclaredConstructors获取构造方法,调用可变参数构造方法进行实例化的,实例化完成之后。接着就要进行依赖注入了,依赖注入(setter方式)的处理入口是populateBean,在处理依赖的时候先将依赖进行转换,进行依赖注入的最终实现是applyPropertyValues方法,这个方法最终使用反射Method实现注入的。依赖的注入部分也是一个比较复杂的处理过程,目前介绍较为混乱,后续章节将会进行详细的介绍。

        码了一大堆文字符号,是时候使用图片揭发和证明真相了。spring ioc容器的默认实现是AbstractApplicationContext,初始化入口是refresh方法,附图spring-4.0.3的spring ioc容器实现图片。

题外话:良好代码组织实例,这样的代码是一个很好的组织风格,首先符合了松耦合高内聚的原则,这个原则不仅仅体现在对象、模块、系统上面,同时更多时候这个原则也体现在面向对象软件的基本组成单元上——类,类里面的基本单元是代码块(一般以成员方法划分,也有游离类中的花括号划分,不细究),成员方法里面的代码就是相同功能构成部分的指令高度集中在成员方法内,而与其他功能的指令(另一成员方法)松耦合,不同功能的耦合仅仅发生在它们组装成更大功能时。高内聚松耦合的代码组织可以让代码灵活复用。上面的代码也体现了这个问题的思考和分析步骤,从上面我可以看到ioc容器工作步骤,阅读起来简单。

简单讲解代码:ioc容器工作有12个步骤,(1)prepareRefresh方法记录初始化时刻和初始化属性资源,(2)obtainFreshBeanFactory实例化beanFactory,并加载、解析和注册元数据,(3)prepareBeanFactory配置beanFactory的标准特性,例如类加载器和bean 后置处理器(postProcessor),(4)postProcessBeanFactory注册与web相关的scope,(5)invokeBeanFactoryPostProcessors实例化并调用所有已经注册的BeanFactoryPostProcessor,(6)registerBeanPostProcessors实例化并调用所有已经注册的BeanPostProcessor,(7)initMessageSource方法初始化消息源,用于国际化,(8)initApplicationEventMulticaster初始化ApplicationEventMulticaster,用于广播所有事件给注册的listener,(9)onRefresh初始化theme,用于spring mvc框架为应用设置统一的静态资源(样式、图片),(10)registerListeners将实现ApplicationListener的bean添加到ApplicationEventMulticaster里面去,(11)finishBeanFactoryInitialization与实例化所有注册的singlon  bean,同时进行依赖注入,(12)finishRefresh调用LifecycleProcessor的onRefresh方法并发布ContextRefreshedEvent事件,至此ioc处于工作状态。

       本文还属于“杂菜煲”,将读取、解析、注册元数据和依赖注入等一并介绍了,大部分工作细节没有进行披露,例如namespace具体的处理过程,mvc namespace如何处理、tx namespace如何处理、aop namespace如何处理、ioc容器事件模型等等,他们都是一个较大的专题,后面会逐个进行介绍的。下一篇博文将会介绍spring aop原理,同时会介绍spring aop如何使用ioc进行配置的。目的是将spring framework工作原理从实现细节上面进行深入学习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值