Spring IOC源码学习总结

最近在学习Spring IOC基本源码,了解整个的流程。代码太绕了,看的时候简单记录了下,现在分享下~

1、首先Spring简单总结

Spring提供了很多轻量级应用开发实践的工具集合,这些工具集以接口、抽象类、或工具类的形式存在于Spring中。通过使用这些工具集,可以实现应用程序与各种开源技术及框架间的友好整合。比如有关jdbc封装的数据访问工具Spring JDBC,有关编写单元测试的spring test包以及spring-mock,有关访问动态脚本语言的Spring Script,另外还有发送邮件的工具Spring Mail、日程及任务处理工具Spring scheduling等。 可以这么说,大多数企业级应用开发中经常涉及到的一些通用的问题,都可以通过Spring提供的一些实用工具包轻松解决
依赖注入的三种方式:
(1)接口注入
(2)Construct注入
(3)Setter注入
控制反转(IoC)与依赖注入(DI)是同一个概念,
引入IOC的目的:
(1)脱开、降低类之间的耦合;
(2)倡导面向接口编程、实施依赖倒换原则; 
(3)提高系统可插入、可测试、可修改等特性。
具体做法:
(1)将bean之间的依赖关系尽可能地抓换为关联关系;
(2)将对具体类的关联尽可能地转换为对 Java  interface的关联,而不是与具体的服务对象相关联;
(3)Bean实例具体关联相关Java interface的哪个实现类的实例,在配置信息的元数据中描述;
(4)由IoC组件(或称容器)根据配置信息,实例化具体bean类、将bean之间的依赖关系注入进来。

2、简单步骤

1、资源定位,即首先要找到applicationContext.xml文件
2、BeanDefinition的载入,把XML文件中的数据统一加载到BeanDefinition中,方便以后的处理
3、向IOC容器中注入BeanDefinition数据
4、将BeanDefinition中的数据进行依赖注入(反射)

3、XmlBeanFactory

  •  public class XmlBeanFactory extends DefaultListableBeanFactory{  
  •      private final XmlBeanDefinitionReader reader;  

  •      public XmlBeanFactory(Resource resource)throws BeansException{  
  •         this(resource, null);  
  •      }          
  •      public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory)  
  •      throws BeansException{  
  •          super(parentBeanFactory);  
  •          this.reader = new XmlBeanDefinitionReader(this);  
  •          this.reader.loadBeanDefinitions(resource);  
  •     }  
  •  }

//根据Xml配置文件创建Resource资源对象,该对象中包含了BeanDefinition的信息
ClassPathResource resource =new ClassPathResource("application-context.xml");

/创建DefaultListableBeanFactory
DefaultListableBeanFactory factory =new DefaultListableBeanFactory();

//创建XmlBeanDefinitionReader读取器,用于载入BeanDefinition。之所以需要BeanFactory作为参数,是因为会将读取的信息回调配置给factory
XmlBeanDefinitionReader reader =new XmlBeanDefinitionReader(factory);

//XmlBeanDefinitionReader执行载入BeanDefinition的方法,最后会完成Bean的载入和注册。完成后Bean就成功的放置到IOC容器当中,以后我们就可以从中取得Bean来使用
reader.loadBeanDefinitions(resource);

4、FileSystemXmlApplicationContext IOC容器初始化

基本步骤:

  1. 资源定位,用字符串或者数组配置BeanDefinition文件(确定文件地址)
  2. 设置资源加载器resourceLoader,AbstractApplicationContext(继承了DefaultResourceLoader)
  3. 开始初始化IOC容器
  4. 初始化的入口在容器实现中的 refresh()调用来完成,实际调用的AbstractRefreshableApplicationContext的refreshBeanFactory()方法
  5. 已有BeanFacotry(ioc容器)则销毁,关闭,重新创建ioc容器
  6. 容器定制化(启动参数,开启注解等),载入Bean定义(BeanDefinition)
  7. 对 bean 定义载入 IOC 容器使用的方法是 loadBeanDefinition,其中的大致过程如下
  8. 通过 ResourceLoader 来完成资源文件位置的定位,DefaultResourceLoader 是默认的实现,同时上下文本身就给出了 ResourceLoader 的实现。
  9. 可以从类路径,文件系统, URL 等方式来定位资源位置,并抽象成相应的Resource 。
  10. 如果是 XmlBeanFactory作为 IOC 容器,那么需要为它指定 bean 定义的资源,也就是说 bean 定义文件是通过抽象成 Resource 来被 IOC 容器处理的。
  11. 容器通过 BeanDefinitionReader来完成定义信息的解析和 Bean 信息的注册,往往使用XmlBeanDefinitionReader 来解析 bean 的 xml 定义文件(Resource),转换成Document对象。
  12. 至此,IOC加载读入了资源文件,并转换成了document对象。下面解析Document对象为Bean,并注册到容器中。
  13. 实际的处理过程是委托给 BeanDefinitionParserDelegate 来完成的,它定义了Spring BeanDefinition XML文件的各种元素,以及解析各种元素的方法。
  14. 如果document对象使用了Spring默认的XML命名空间,则按照spring的解析规则,解析<import>导入元素,解析<Alias>别名元素以及普通的<Bean>元素。否则使用用户自定义的解析规则。
  15. 将Document对象以及Resource文件转换为Spring IoC所识别的数据结构——BeanDefinition,它是Bean定义资源文件中配置的POJO对象在Spring IoC容器中的映射。
  16. 容器解析得到 BeanDefinition 以后,需要把它在 IOC 容器中注册,这由 IOC 实现 BeanDefinitionRegistry 接口来实现。
  17. 注册过程就是在 IOC 容器内部维护的一个HashMap 来保存得到的 BeanDefinition。这个 HashMap 是 IoC 容器持有 bean 信息的场所,以后对 bean 的操作都是围绕这个HashMap 来实现的。注册过程中需要线程同步,注册后重载BeanDefinition的缓存。
  18. 注册完成,IoC容器就建立了整个Bean的配置信息,这些BeanDefinition信息已经可以使用,并且可以被检索,IoC容器的作用就是对这些注册的BeanDefinition进行处理和维护。

详细步骤:

1、ApplicationContext =new FileSystemXmlApplicationContext(xmlPath);
2、设置资源加载器和资源定位
  • 父类容器的构造方法AbstractApplicationContext(ApplicationContext parent)为容器设置好Bean资源加载器 PathMatchingResourcePatternResolver
  • setConfigLocations(configLocations)方法设置Bean定义资源文件的定位路径。既可以使用一个字符串来配置多个Spring Bean定义资源文件,也可以使用字符串数组
  • refresh(); 载入Bean定义(见3)
3、AbstractApplicationContext的refresh函数载入Bean定义 refresh() throws BeansException, IllegalStateException
  • synchronized (this.startupShutdownMonitor)
  • 调用容器准备刷新的方法,获取容器的当时时间,同时给容器设置同步标识 prepareRefresh();
  • 告诉子类启动refreshBeanFactory()方法,启动Bean定义资源文件的载入(见4)
  • 为BeanFactory配置容器特性,例如类加载器、事件处理器等 prepareBeanFactory(beanFactory);
  • 为BeanFactory初始化信息源,件传播器等等
refresh()方法的作用是:在创建IoC容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在refresh之后使用的是新建立起来的IoC容器。refresh的作用类似于对IoC容器的重启,在新建立好的容器中对容器进行初始化,对Bean定义资源进行载入。refresh()方法主要为IoC容器Bean的生命周期管理提供条件,Spring IoC容器载入Bean定义资源文件从其子类容器refreshBeanFactory()方法启动
4、AbstractApplicationContext子类的refreshBeanFactory()方法
  • 先判断BeanFactory是否存在,如果存在则先销毁beans并关闭beanFactory
  • 接着创建DefaultListableBeanFactory,完成定制化(启动参数,开启注解等)
  • 委派子类AbstractXmlApplicationContext调用loadBeanDefinitions(beanFactory)装载bean 定义
5、其子类AbstractXmlApplicationContext对loadBeanDefinitions方法的实现
  • 用beanFactory创建了XmlBeanDefinitionReader
  • 将自身(XmlBeanDefinitionReader )设置为Reader 的ResourceLoader
  • 用getConfigLocations方法获得资源位置location
  • 用Reader调用XmlBeanDefinitionReader 父类的loadBeanDefinitions方法,参数为location (见6)
6、XmlBeanDefinitionReader 父类AbstractBeanDefinitionReader的loadBeanDefinitions(location)方法
  • 获得ResourceLoader(即5中的XmlBeanDefinitionReader)
  • 用ResourceLoader调用父类DefaultResourceLoader方法getResources(location)获得BeanDefinitionResources(或者数组resources) (见7)
  • 委派子类XmlBeanDefinitionReader调用loadBeanDefinitions(resources)(见8)
7、XmlBeanDefinitionReader父类DefaultResourceLoader方法getResources(location)
  • ClassPathResource
  • UrlResource
  • ResourceByPath
8、XmlBeanDefinitionReader调用loadBeanDefinitions(resources)
  • 对XML进行特殊编码处理,并调用重载方法loadBeanDefinitions(new EncodedResource(resource))
  • 将encodedResource转换为XML的解析源InputSource
  • 调用doLoadBeanDefinitions(inputSource, encodedResource.getResource())完成了
    • 将XML文件转换为DOM对象Document(见9)
    • 启动对Bean定义解析registerBeanDefinitions(doc, resource)(见10)
9、DocumentLoader将Bean定义资源转换为Document对象
  • 创建文件解析器工厂DocumentBuilderFactory
  • 创建文档解析器DocumentBuilder(factory, entityResolver, errorHandler)
  • 解析Spring的Bean定义资源返回Document对象 builder.parse(inputSource)
至此Spring IoC容器根据定位的Bean定义资源文件,将其加载读入并转换成为Document对象过程完成。接下来我们要继续分析如何将Document对象解析为Spring IoC管理的Bean对象并将其注册到容器中的。
10、XmlBeanDefinitionReader解析载入的Bean定义资源文件registerBeanDefinitions(Document doc, Resource resource) 即按照Spring的Bean语义要求将Bean定义资源解析并转换为容器内部数据结构
  • 创建BeanDefinitionDocumentReader来对xml格式的BeanDefinition解析
  • 解析过程入口,委派实现类DefaultBeanDefinitionDocumentReader完成registerBeanDefinitions(doc, createReaderContext(resource)) (见11)
  • 统计解析的Bean数量 getRegistry().getBeanDefinitionCount() - countBefore
11、DefaultBeanDefinitionDocumentReader对Bean定义的Document对象解析(根据Spring DTD对Bean的定义规则解析)registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
  • 获取Document的根元素doc.getDocumentElement();
  • 调用方法createHelper(readerContext, root)获得BeanDefinitionParserDelegate,定义了Spring Bean定义XML文件的各种元素
  • 解析Bean定义之前,进行自定义的解析 preProcessXml(root);
  • 调用方法parseBeanDefinitions(root, delegate);从Document的根元素开始解析Bean定义
  • 在解析Bean定义之后,进行自定义的解析 postProcessXml(root);
方法 parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate),从Document的根元素开始解析Bean定义
  • Bean定义的Document对象使用了Spring默认的XML命名空间,遍历Document对象根元素的所有子节点,如果是XML元素节点,调用parseDefaultElement(ele, delegate);否则则使用用户自定义的解析规则解析元素节点
  • 根节点没有使用Spring默认的命名空间,则使用用户自定义的解析规则解析Document根节点
方法 parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate),使用Spring的Bean规则解析Document元素节点
  • 如果元素节点是<Import>导入元素,进行导入解析 importBeanDefinitionResource(ele)
  • 如果元素节点是<Alias>别名元素,进行别名解析 processAliasRegistration(ele);
  • 元素节点既不是导入元素,也不是别名元素,即普通的<Bean>元素,按照Spring的Bean规则解析元素 processBeanDefinition(ele, delegate);(见12)
方法 importBeanDefinitionResource(Element ele),解析<Import>导入元素,从给定的导入路径加载Bean定义资源到Spring IoC容器中
  • 获取给定的导入元素的location属性 ele.getAttribute(RESOURCE_ATTRIBUTE);
  • 使用系统变量值解析location属性值 location = SystemPropertyUtils.resolvePlaceholders(location);
  • 导入元素的location是否是绝对路径
  • 绝对路径 使用资源读入器加载给定路径的Bean定义资源 getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
  • 相对路径 将给定导入元素的location封装为相对路径资源 Resource relativeResource =
  • getReaderContext().getResource().createRelative(location);
  • 相对路径资源存在 使用资源读入器加载Bean定义资源,不存在 根据Spring IoC容器资源读入器的基本路径加载
  • 在解析完<Import>元素之后,发送容器导入其他资源处理完成事件
方法 processAliasRegistration(Element ele) 解析<Alias>别名元素,为Bean向Spring IoC容器注册别名
  • 获取<Alias>别名元素中name的属性值 String name = ele.getAttribute(NAME_ATTRIBUTE);
  • 获取<Alias>别名元素中alias的属性值 String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
  • 都不为空时,向容器的资源读入器注册别名 getReaderContext().getRegistry().registerAlias(name, alias);
  • 在解析完<Alias>元素之后,发送容器别名处理完成事件
方法 processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate),解析Bean定义资源Document对象的普通元素
  • delegate.parseBeanDefinitionElement(ele);对Document对象中<Bean>元素的解析,获得对BeanDefinition的封装类,BeanDefinitionHolder(见12-15)
  • 向Spring IoC容器注册解析得到的Bean定义 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); (见16)
  • 完成向Spring IoC容器注册后发送注册事件,getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
12、BeanDefinitionParserDelegate解析Bean定义资源文件中的<Bean>元素,这个方法中主要处理<Bean>元素的id,name和别名属性 parseBeanDefinitionElement(Element ele)
  • 获取<Bean>元素中的id属性值、name、alias,将所有name属性值存放到别名中
  • 如果<Bean>元素中没有配置id属性时,将别名中的第一个值赋值给beanName
  • 详细对<Bean>元素中配置的Bean定义解析beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
  • 如果beanDefinition 中不包含beanName(即没有id、别名、name),且没有子<Bean>,需要生成一个唯一的baenName并注册,有子<Bean>则使用别名+类名后缀作为baenName并注册。
方法 parseBeanDefinitionElement( Element ele, String beanName, BeanDefinition containingBean)详细对<Bean>元素中配置的Bean定义除id、name和别名以外的其他属性数据进行解析
  • 获得class名字,parent属性(如果有)
  • 根据<Bean>元素配置的class名称和parent属性值创建BeanDefinition AbstractBeanDefinition bd = createBeanDefinition(className, parent);
  • 解析一些属性,如配置的单态(singleton)属性,解析description信息,meta(元信息)等
  • 解析<Bean>元素的<property>设置(见13)
注意:在解析<Bean>元素过程中没有创建和实例化Bean对象,只是创建了Bean对象的定义类
BeanDefinition,将<Bean>元素中的配置信息设置到BeanDefinition中作为记录,当依赖注入时才使用这些记录信息创建和实例化具体的Bean对象。
13、BeanDefinitionParserDelegate解析<property>元素 parsePropertyElements(Element beanEle, BeanDefinition bd)
  • 获取<Bean>元素中所有的子元素,如果子元素是<property>,则调用方法 解析<property>子元素
  • 通过一个parseState队列存储所有property键值对,this.parseState.push(new PropertyEntry(propertyName));
  • 获取<property>元素的名字,如果一个Bean中已经有同名的property存在,则不进行解析,直接返回。即如果在同一个Bean中配置同名的property,则只有第一个起作用。
  • 解析获取property的值,方法 Object val = parsePropertyValue(ele, bd, propertyName);
  • 根据property的名字和值创建property实例
方法 解析获取property值 Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName)
  • 获取<property>,只能是其中一种类型:ref,value,list等
  • 如果属性是ref,创建一个ref的数据对象RuntimeBeanReference,封装了ref信息。一个指向运行时所依赖对象的引用RuntimeBeanReference ref = new RuntimeBeanReference(refName);并调用ref.setSource(extractSource(ele));与当前的property对象关联。
  • 如果属性是value,创建一个value的数据对象TypedStringValue,封装了value信息。TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE)); valueHolder.setSource(extractSource(ele));与当前的property对象关联
  • 如果当前<property>元素还有子元素,解析<property>的子元素(见14)
14、解析<property>元素的子元素 BeanDefinitionParserDelegate类中的
Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType)
  • 如果<property>没有使用Spring默认的命名空间,则使用用户自定义的规则解析
  • 如果子元素是bean,则使用解析<Bean>元素的方法解析
  • 如果子元素是ref,ref中只能有以下3个属性:bean、local、parent。bean引用其他解析的Bean的名称可以不再同一个Spring配置文件中;local属性值,引用同一个Xml文件中配置;parent属性值,引用父级容器中的Bean。创建ref类型数据,与当前的property对象关联
  • 如果子元素是value、null、array、list……,使用相应的方法解析,生成对应的数据对象,比如ManagedList、ManagedArray、ManagedSet等,这些Managed类是Spring对象BeanDefiniton的数据封装(见15)
15、解析<list>子元素 parseListElement(Element collectionEle, BeanDefinition bd)
  • 获取数据类型
  • 获取所有子节点 nl
  • 新建ManagedList<Object> target,设置类型
  • 具体的<list>元素解析 parseCollectionElements(nl, target, bd, defaultElementType); <array>、<list>和<set>都使用该方法解析
经过对Spring Bean定义资源文件转换的Document对象中的元素层层解析,Spring IoC现在已经将XML形式定义的Bean定义资源文件转换为Spring IoC所识别的数据结构——BeanDefinition,它是Bean定义资源文件中配置的POJO对象在Spring IoC容器中的映射。但是最为重要的依赖注入还没有发生,即向容 器注册BeanDefinition信息。
16、解析过后的BeanDefinition在IoC容器中的注册 registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
  • 获取解析的BeanDefinition的名称
  • 向IoC容器注册BeanDefinition registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition())(见17)
  • 如果解析的BeanDefinition有别名,向容器为其注册别名
17、DefaultListableBeanFactory向IoC容器注册解析后的BeanDefinition registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException
  • 使用一个HashMap的集合对象存放IoC容器中注册解析的BeanDefinition
  • 校验解析的BeanDefiniton,失败抛出异常
  • 注册的过程中需要线程同步 synchronized (this.beanDefinitionMap)
  • 检查是否有同名的BeanDefinition已经在IoC容器中注册,已经注册不允许覆盖就抛出异常,允许覆盖则覆盖。IoC容器中没有已经注册同名的Bean,按正常注册流程注册。
  • 注册后重置所有已经注册过的BeanDefinition的缓存
至此,Bean定义资源文件中配置的Bean被解析过后,已经注册到IoC容器中,被容器管理起来,真正完成了IoC容器初始化所做的全部工作。
现在IoC容器中已经建立了整个Bean的配置信息,这些BeanDefinition信息已经可以使用,并且可以被检索,IoC容器的作用就是对这些注册的Bean定义信息进行处理和维护。接下来进行IOC的依赖注入。


源码在这里阅读的:http://www.itnose.net/detail/6669144.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值