xmlBeanFactory 加载注册bean流程
(一) Spring bean注册流程
1.定义好spring的配置文件
2.通过Resource对象将Spring配置文件进行抽象,抽象成一个具体的Resource对象,如ClassPathResource
3.定义好要使用的Bean工厂(各种BeanFactory)
4.定义好XmlBeanDefinitionReader对象,并将工厂对象作为参数传递进去,从而构建好两者之间的关联关系。
5.通过XmlBeanDefinitionReader对象读取之前所抽取出的Resource对象
6.流程开始解析
7.针对XML文件进行各种元素以及元素属性的解析,这里面,真正的解析是通过BeanDefinitionParseDelegate对象来完成的(委托模式)
8.通过BeanDefinitionParseDelegate对象解析XML文件实,又是用到了模板方法设计模式(pre,process,post)
9.当所有的bean标签元素都解析完成后,开始定义一个BeanDefinition对象,该对象是一个非常重要的对象,里面容纳了一个Bean相关的所有属性
10.Bean Definition对象创建完毕后,Spring又会创建一个BeanDefinitionHolder对象来持有这个BeanDefinition对象。
11.BeanDefinitionHolder主要包括两部分内容:beanName和BeanDefinition
12.工厂将解析出来的Bean信息存取到内部的一个ConcurrentMap中,该Man的键是BeanName,value是BeanDefinition对象。
13.调用Bean解析完毕的出发动作,从而触发相应的监听器的方法的执行(使用到了观察者模式)
注册bean对象过程:
BeanDefinitionParserDelegate类中涉及到几个关键对象:BeanDefinitionHolder,AbstractBeanDefinition,GenericBeanDefinition。
1 GenericBeanDefinition:要解析属性首先要创建用于承载属性的实例,也就是创建GenericBeanDefinition类型的实例。
2 BeanDefinition是配置文件<bean>元素标签在容器中的内部表示形式。<bean>元素标签拥有class、scope、lazy-init等配置属性,BeanDefinition则提供了相应的beanClass、scope、lazyInit属性,BeanDefinition和<bean>中的属性是一一对应的。Spring通过BeanDefinition将配置文件中的<bean>配置信息转换为容器的内部表示,并将这些BeanDefiniton注册到BeanDefinitonRegistry中。Spring容器的BeanDefinitionRegistry就像是Spring配置信息的内存数据库,主要是以map的形式保存,后续操作直接从BeanDefinition Registry中读取配置信息。
3 通过beanDefinition,beanName,aliases,来封装 BeanDefinitionHolder.
自定义元素解析:
自定义元素解析过程首先是寻找元素对应的解析器,随后就是获取在自定义的Handler类中的init方法中注册的对应的UserBeanDefinitionParser实例,并调用其parse方法进行进一步解析。
虽说是对自定义配置文件的解析,但是,大部分精力是用来处理将解析后的AbstractBeanDefinition转化为BeanDefinitionHolder并注册的功能,而真正去做解析的事情委托给了函数parseInternal,正是这句代码调用了我们自定义的解析函数。在parseInternal中并不是直接调用自定义的doParse函数,而是进行了一系列的数据准备,包括对beanClass、scope、lazyInit等属性的准备。
(二)Spring bean加载:
spring bean的创建整体流程:
1.Spring所管理的bean实际是缓存在一个ConcurrentMap中(SingletonObjects对象中)
2.该Map的键是BeanName,value是BeanDefinition对象。
3.在创建Bean之前,首先需要将该Bean的创建标识制定好,表示该Bean已经或者即将被创建,目的是增强缓存的效率。
4.根据Bean的scope属性来确定当前创建的Bean是一个Singleton还是Prototype的Bean,然后创建相应的对象。
5.无论是Singleton还是Prototype的Bean,其创建流程是一致的。
6.通过Java反射机制来创建bean的实例,在创建之前需要检查构造方法的访问修饰符,如果不是public的,则会调用setAccessible(true)方法来突破Java的语法限制,使得可以通过非public构造方法来完成对象实例的创建
7.当对象创建完毕后,开始对象属性的注入(populateBean方法实现)
8.在对象属性注入的过程中,Spring除去使用之前通过beanDefinition对象获取的bean信息之外,还会通过反射的方式获取到上面所创建的bean中的真实属性信息(还包括一个class属性,表示该bean所对应的class类型)
9.完成bean属性的注入(或者抛出异常)
10.如果bean是一个单例的,那么会将所创建出来的Bean添加到SingletonObjects对象中,使程序后续再次使用。
bean加载的主要过程:
(1)转换对应beanName :
因为传入的参数name可能是别名,也可能是FactoryBean,所以需要进行一系列的解析,这些解析内容包括如下内容。
● 去除FactoryBean的修饰符,也就是如果name="&aa",那么会首先去除&而使name="aa"。
● 取指定alias所表示的最终beanName,例如别名A指向名称为B的bean则返回B;若别名A指向别名B,别名B又指向名称为C的bean则返回C。
(2) 尝试从缓存中加载单例 :
单例在Spring的同一个容器内只会被创建一次,后续再获取bean,就直接从单例缓存中获取了。当然这里也只是尝试加载,首先尝试从缓存中singletonObjects加载,如果加载不成功则再次尝试从singletonFactories中加载, singletonObjects与singletonFactories都是DefaultSingletonBeanRegistry的成员变量。
(因为在创建单例bean的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,在Spring中创建bean的原则是不等bean创建完成就会将创建bean的ObjectFactory提早曝光加入到缓存中,一旦下一个bean创建时候需要依赖上一个bean则直接使用ObjectFactory).
(3) bean的实例化 :
如果从缓存中得到了bean的原始状态,则需要对bean进行实例化。这里有必要强调一下,缓存中记录的只是最原始的bean状态,并不一定是我们最终想要的bean。
举个例子,假如我们需要对工厂bean进行处理,那么这里得到的其实是工厂bean的初始状态,但是我们真正需要的是工厂bean中定义的factory-method方法中返回的bean,而getObjectForBeanInstance就是完成这个工作的.
(4) 原型模式的依赖检查 :
原型模式的依赖检查。只有在单例情况下才会尝试解决循环依赖,如果存在A中有B的属性,B中有A的属性,那么当依赖注入的时候,就会产生当A还未创建完的时候因为对于B的创建再次返回创建A,造成循环依赖,也就是情况:isPrototypeCurrentlyInCreation(beanName)判断true。
(5) 检测parentBeanFactory
源代码对两个条件进行了判断,parentBeanFactory != null &&!containsBean Definition (beanName),parentBeanFactory != null。parentBeanFactory如果为空,则其他一切都是浮云,这个没什么说的,但是!containsBeanDefinition(beanName)就比较重要了,它是在检测如果当前加载的XML配置文件中不包含beanName所对应的配置,就只能到parentBeanFactory去尝试下了,然后再去递归的调用getBean方法。
(6) 将存储XML配置文件的GernericBeanDefinition转换为RootBeanDefinition :
因为从XML配置文件中读取到的Bean信息是存储在GernericBeanDefinition中的,但是所有的Bean后续处理都是针对于RootBeanDefinition的,所以这里需要进行一个转换,转换的同时如果父类bean不为空的话,则会一并合并父类的属性。
(7) 寻找依赖 :
因为bean的初始化过程中很可能会用到某些属性,而某些属性很可能是动态配置的,并且配置成依赖于其他的bean,那么这个时候就有必要先加载依赖的bean,所以,在Spring的加载顺寻中,在初始化某一个bean的时候首先会初始化这个bean所对应的依赖。
(8) 针对不同的scope进行bean的创建 :
我们都知道,在Spring中存在着不同的scope,其中默认的是singleton,但是还有些其他的配置诸如prototype、request之类的。在这个步骤中,Spring会根据不同的配置进行不同的初始化策略。
(9) 类型转换:
通常对该方法的调用参数requiredType是为空的,但是可能会存在这样的情况,返回的bean其实是个String,但是requiredType却传入Integer类型,那么这时候本步骤就会起作用了,它的功能是将返回的bean转换为requiredType所指定的类型。当然,String转换为Integer是最简单的一种转换,在Spring中提供了各种各样的转换器,用户也可以自己扩展转换器来满足需求。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)