IOC容器就像是一个工厂,里面有很多流水线生产出一个个产品(bean)。bean的加载流程大概分为:

  • 容器启动阶段
  • bean加载阶段

容器启动阶段:

1.配置元信息
当你生产物品的时候总得知道产品得规格型号信息,元信息就是这个意思。来源于XML文件,注解等。

2.BeanDefination:
相当于java中得pojo,当元信息被加载到内存以后就是以BeanDefination的形式存在。

3、BeanDefinationReader:如果我们要读取xml配置元信息,那么可以使用XmlBeanDefinationReader。如果我们要读取properties配置文件,那么可以使用PropertiesBeanDefinitionReader加载。而如果我们要读取注解配置元信息,那么可以使用 AnnotatedBeanDefinitionReader加载。我们也可以很方便的自定义BeanDefinationReader来自己控制配置元信息的加载。总的来说,BeanDefinationReader的作用就是加载配置元信息,并将其转化为内存形式的BeanDefination,并存在BeanDefinationRegistry中。通过键值对的方式,键是通过特定的bean定义的id,映射到相应的BeanDefination。

4、BeanDefinationRegistry:

执行到这里,Spring已经将存在于各处的配置元信息加载到内存,并转化为BeanDefination的形式,这样我们需要创建某一个对象实例的时候,找到相应的BeanDefination然后创建对象即可。那么我们需要某一个对象的时候,去哪里找到对应的BeanDefination呢?这种通过Bean定义的id找到对象的BeanDefination的对应关系或者说映射关系又是如何保存的呢?这就引出了BeanDefinationRegistry了。

Spring通过BeanDefinationReader将配置元信息加载到内存生成相应的BeanDefination之后,就将其注册到BeanDefinationRegistry中,BeanDefinationRegistry就是一个存放BeanDefination的大篮子,它也是一种键值对的形式,通过特定的Bean定义的id,映射到相应的BeanDefination。

5、BeanFactoryPostProcessor:

BeanFactoryPostProcessor是容器启动阶段Spring提供的一个扩展点,主要负责对注册到BeanDefinationRegistry中的一个个的BeanDefination进行一定程度上的修改与替换。例如我们的配置元信息中有些可能会修改的配置信息散落到各处,不够灵活,修改相应配置的时候比较麻烦,这时我们可以使用占位符的方式来配置。例如配置Jdbc的DataSource连接的时候可以这样配置:

<bean id="dataSource"  class="org.apache.commons.dbcp.BasicDataSource"  destroy-method="close">  <property name="maxIdle" value="${jdbc.maxIdle}"></property>  <property name="maxActive" value="${jdbc.maxActive}"></property>  <property name="maxWait" value="${jdbc.maxWait}"></property>  <property name="minIdle" value="${jdbc.minIdle}"></property>  <property name="driverClassName"  value="${jdbc.driverClassName}">  </property>  <property name="url" value="${jdbc.url}"></property>  <property name="username" value="${jdbc.username}"></property>  <property name="password" value="${jdbc.password}"></property>
</bean>

BeanFactoryPostProcessor就会对注册到BeanDefinationRegistry中的BeanDefination做最后的修改,替换$占位符为配置文件中的真实的数据。

二、Bean的获取阶段
在容器的启动阶段就完成了bean的注册,以BeanDefination储存在BeanDefinationRegister中,如果不是选择懒加载(BeanFactory是懒加载)的话,在容器启动完成后执行finishBeanFactoryInitialization()方法,隐式的调用所有对象的getBean()方法实例化所有配置的Bean,完成类的加载。
1.doGetBean():bean的获取
doGetBean的方法就是创建Bean之前,先去缓存中或BeanFactory工厂中查看是否存在bean,如果存在,则返回,不存在则进行相应的创建流程。

  1. 检查是不是别名,需要进行解析转换
  2. 尝试从缓存中加载实例,如果获取直接返回,在缓存中获取的bean是原始bean还需要进行实例化,如合并RootBeandefination、BeanPostProcessor进行实例前置处理,实例化,实例后置处理。
  3. 如果缓存中没有对应的bean,则先进行依赖循环检查,存在则报错,如果缓存中没有数据,就会转到父类加工厂(有点像双亲委任制),如果当前的xml文件不报换beanname所对应的配置,就只能到父类工厂中尝试加载
  4. 存储XML配置文件的GernericBeanDefinition转换成RootBeanDefinition,即为合并父类定义。
  5. 初始化依赖的bean,即进行被依赖bean的实例化初始化流程。
  6. 创建bean,默认是单例
protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)throws BeansException {/*1、转换beanName(别名转换):传入的参数name可能只是别名,也可能是FactoryBean,所以需要进行解析转换:(1)消除修饰符,比如工厂引用前缀 String FACTORY_BEAN_PREFIX = "&";(2)解决spring中alias标签的别名问题*/ final String beanName = transformedBeanName(name);Object bean;//2、尝试从缓存中去加载实例,如果获取到了就直接返回// Eagerly check singleton cache for manually registered singletons.Object sharedInstance = getSingleton(beanName);//如果缓存中存在对应的beanif (sharedInstance != null && args == null) {if (logger.isDebugEnabled()) {if (isSingletonCurrentlyInCreation(beanName)) {logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +"' that is not fully initialized yet - a consequence of a circular reference");}else {logger.debug("Returning cached instance of singleton bean '" + beanName + "'");}}//3、缓存渠道的bean的实例化。从缓存中获取的bean是原始状态的bean,需要在这里对bean进行bean实例化。// 此时会进行 合并RootBeanDefinition、BeanPostProcessor进行实例前置处理、实例化、实例后置处理。bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);}// 如果缓存中没有对应beanelse { //4、循环依赖检查。 (构造器的循环依赖)循环依赖存在,则报错。// Fail if we're already creating this bean instance:// We're assumably within a circular reference.if (isPrototypeCurrentlyInCreation(beanName)) {throw new BeanCurrentlyInCreationException(beanName);}// 5、如果缓存中没有数据,就会转到父类工厂去加载//获取父工厂// Check if bean definition exists in this factory.BeanFactory parentBeanFactory = getParentBeanFactory();//!containsBeanDefinition(beanName)就是检测如果当前加载的xml配置文件中不包含beanName所对应的配置,就只能到parentBeanFacotory去尝试加载bean。if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {// Not found -> check parent.String nameToLookup = originalBeanName(name);if (args != null) {// Delegation to parent with explicit args.return (T) parentBeanFactory.getBean(nameToLookup, args);}else {// No args -> delegate to standard getBean method.return parentBeanFactory.getBean(nameToLookup, requiredType);}}if (!typeCheckOnly) {markBeanAsCreated(beanName);}//6、存储XML配置文件的GernericBeanDefinition转换成RootBeanDefinition,即为合并父类定义。try {// XML配置文件中读取到的bean信息是存储在GernericBeanDefinition中的,但Bean的后续处理是针对于RootBeanDefinition的,所以需要转换后才能进行后续操作。final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);checkMergedBeanDefinition(mbd, beanName, args);// Guarantee initialization of beans that the current bean depends on.//7、初始化依赖的beanString[] dependsOn = mbd.getDependsOn();//bean中可能依赖了其他bean属性,在初始化bean之前会先初始化这个bean所依赖的bean属性。if (dependsOn != null) {for (String dependsOnBean : dependsOn) {if (isDependent(beanName, dependsOnBean)) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Circular depends-on relationship between '" + beanName + "' and '" + dependsOnBean + "'");}registerDependentBean(dependsOnBean, beanName);getBean(dependsOnBean);}}//8、创建bean// Create bean instance.if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {@Overridepublic Object getObject() throws BeansException {try {return createBean(beanName, mbd, args);}catch (BeansException ex) {// Explicitly remove instance from singleton cache: It might have been put there// eagerly by the creation process, to allow for circular reference resolution.// Also remove any beans that received a temporary reference to the bean.destroySingleton(beanName);throw ex;}}});bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}else if (mbd.isPrototype()) {// It's a prototype -> create a new instance.Object prototypeInstance = null;try {beforePrototypeCreation(beanName);prototypeInstance = createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);}else {String scopeName = mbd.getScope();final Scope scope = this.scopes.get(scopeName);if (scope == null) {throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");}try {Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {@Overridepublic Object getObject() throws BeansException {beforePrototypeCreation(beanName);try {return createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}}});bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);}catch (IllegalStateException ex) {throw new BeanCreationException(beanName,"Scope '" + scopeName + "' is not active for the current thread; consider " +"defining a scoped proxy for this bean if you intend to refer to it from a singleton",ex);}}}catch (BeansException ex) {cleanupAfterBeanCreationFailure(beanName);throw ex;}}// Check if required type matches the type of the actual bean instance.if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {try {return getTypeConverter().convertIfNecessary(bean, requiredType);}catch (TypeMismatchException ex) {if (logger.isDebugEnabled()) {logger.debug("Failed to convert bean '" + name + "' to required type [" +ClassUtils.getQualifiedName(requiredType) + "]", ex);}throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}}return (T) bean;}

2.在缓存中获取单例bean 调用getSingleton(beanname,allowearlyReference)方法
1).从一级缓存中获取,若为空,则检查是否在创建中
2).检查的时候要加锁,防止并发创建
3).从二级bean中获取bean,如果正在加载,则不处理,否则调用addSingletonFactory 方法将对应的objectFactory 初始化策略存储在 singletonFactories,也就是三级缓存中,三级缓存存储的也是早期bean。在解决循环依赖中,三级缓存是可以对早期的类对象进行自定义处理,将处理完的对象,放在二级缓存中,若还有循环依赖的处理,拿的是二级循环中的对象。

protected Object getSingleton(String beanName, boolean allowEarlyReference) {// 从缓存池中获取bean:singletonObjects 一级缓Object singletonObject = this.singletonObjects.get(beanName);// 如果一级缓存中为null,再判断是否正在创建if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {// 加锁,防止并发创建synchronized (this.singletonObjects) {// 从二级缓存中获取bean,如果此 bean 正在加载则不处理singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {// 当某些方法需要提前初始化,调用 addSingletonFactory 方法将对应的objectFactory 初始化策略存储在 singletonFactoriesObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}return singletonObject;
}

Bean实例化阶段执行的顺序

Spring的bean加载流程相关推荐

  1. 【Spring源码分析】Bean加载流程概览

    代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...

  2. beaninfo详解源码解析 java_【Spring源码分析】Bean加载流程概览

    代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...

  3. Spring源码分析:Bean加载流程概览及配置文件读取

    很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事的都是Java Web的工作,对于程序员来说,一个Web项目用到Spring,只是配置一下配置文件而已 ...

  4. Spring控制Bean加载顺序

    spring容器载入bean顺序是不确定的,spring框架没有约定特定顺序逻辑规范. 首先要了解depends-on或@DependsOn作用,是用来表示一个bean A的实例化依赖另一个bean ...

  5. spring cloud feign 加载流程

    一.如果只想加入feign,不要载入hystrix,则在引包时排除掉hystrix的包. <dependency><groupId>org.springframework.cl ...

  6. spring boot实战(第十篇)Spring boot Bean加载源码分析

    前言 前面的文章描述了Application对应Bean的创建,本篇将阐述spring boot中bean的创建过程 refresh 首先来看SpringApplication#run方法中refre ...

  7. Spring框架—SpringBean加载过程

    原文作者:RunAlgorithm 原文地址:图文并茂,揭秘 Spring 的 Bean 的加载过程 1. 概述 Spring 作为 Ioc 框架,实现了依赖注入,由一个中心化的 Bean 工厂来负责 ...

  8. 深入Spring:自定义注解加载和使用

    转自:https://blog.csdn.net/z69183787/article/details/53784845 前言 在工作中经常使用Spring的相关框架,免不了去看一下Spring的实现方 ...

  9. 看看Spring的源码(一)——Bean加载过程

    首先Web项目使用Spring是通过在web.xml里面配置 org.springframework.web.context.ContextLoaderListener初始化IOC容器的. <l ...

最新文章

  1. 利用gcc的-finstrument-functions获取函数轨迹跟踪
  2. Linux操作命令(四)
  3. 第二次作业--熟悉使用工具
  4. 链表最终总结【数据结构】
  5. 国家开放大学计算机应用模块3客观题答案,国家开放大学《计算机应用基础》考试与答案形考任务模块3模块3Excel2010电子表格系统—客观题答案.pdf...
  6. Docker学习与总结
  7. 正式发布! .NET开发控件集ComponentOne 新版本加入Blazor UI
  8. Unity3D研究院之Prefab里面的Prefab关联问题
  9. OpenCV官方网站:这里可以白嫖教程、检索API、下载例程
  10. BTP-2118 玩 GTA5
  11. Markdown博客系统的搭建与使用
  12. CSS z-index与JQ fadeOut()缓动效果无效问题
  13. 已解决pandas创建DataFrame对象失败
  14. 【Red Team——基础】通过钓鱼攻击获得访问权限
  15. 搜狗输入法,输英语单词自动提示
  16. 李天平:技术以外的功夫
  17. RTSP 和 RTMP原理 通过ffmpeg实现将本地摄像头推流到RTSP服务器
  18. 测试用例模板(个人习惯使用)
  19. 如何使用计算机的加减乘除,计算机是怎么懂加减乘除的
  20. nyoj 125 盗梦空间

热门文章

  1. 用脑过度了头痛了两天
  2. 敏捷软件开发总结——什么是敏捷设计
  3. TreeView 控件节点上下移动
  4. 【Python 百练成钢】灯光模拟
  5. latex公式大括号的使用方法
  6. 利用堆实现堆排序优先队列
  7. 10__jsp入门el表达式入门CookieSession
  8. 由老旧计算机引出的麻烦事儿
  9. WPF:Margin属性和Padding属性
  10. myeclipse trial expired