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