Spring源码分析三:bean的加载

bean的加载

上面都是容器的初始化,并不是bean的初始化,放入map里面的都只是BeanDefinition

还是简单的demo:

public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = (Student) ctx.getBean("student");
        System.out.println(student.getName());
    }

AbstractApplicationContext的getBean方法

可以看到就只有两个方法
在这里插入图片描述

assertBeanFactoryActive()

在这里插入图片描述

可以看到在方法assertBeanFactoryActive中,只不过是判断下当前的Spring容器,是否被激活了或者是否被关闭了,如果容器还未被激活或发现容器已经关闭了就会抛出异常。

这样做也是合情合理的,毕竟如果Spring容器关闭了或者还未初始化,也就没法让它去加载bean了;

getBeanFactory().getBean(name)

可以看到,接下来会通过getBeanFactory方法获取ApplicationContext中的Spring初级容器BeanFactory,然后再调用BeanFactory中的getBean方法去加载bean。

首先,通过getBeanFactory方法获取Spring初级容器这一点,在前面分析ApplicationContext初始化时,已经看到BeanFactory是如何初始化的了。

对于初级容器和高级容器而言其实都是一样的,最后都是从初级容器的getBean方法开始的。

在这里插入图片描述

关键方法doGetBean

doGetBean方法分析

在这里插入图片描述

transformedBeanName转换并得到bean的最终名称

protected String transformedBeanName(String name) {
	return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}
  • BeanFactoryUtils.transformedBeanName(name):
    • 在这里插入图片描述

    • 可以看到,在BeanFactoryUtils的方法transformedBeanName中其实就干了一件事,如果发现name以一个或多个符号"&“为前缀,那就剔除掉name前缀中的”&",直到name前缀中的符号“&"都剔除干净了才返回name。

  • canonicalName
    • 在这里插入图片描述

    • 在方法canonicalName中,其实就是通过参数传进来的name,到别名缓存alilasMap中获取bean的实际名称,之前在BeanDefinition注册到Spring容器之后,也看到了别名的注册逻辑。

    • canonicalName方法的处理也比较好理解,因为我们是通过getBean方法,让Spring来实例化name对应的bean,那Spring肯定要确保你传进来的bean名称name,得要尽最大的可能获取到Spring容器中的BeanDefinition。

    • 当然,如果用户调用getBean方法时传进来一些错误的name,Spring是无可奈何的,肯定是获取不到相应的BeanDefinition来实例化bean的,但是,如果你传进来的name是bean的别名,那Spring还是有一定的纠错能力的,而方法canonicalName就是Spring将别名转换为bean实际名称的一个方法。

      • 比如当bean在注册BeanDefinition时,BeanDefinition相应注册的名称为student,但是,如果这个bean的别名同时又被设置为student1,这个时候如果你通过student1这个别名,去Spring容器中获取BeanDefinition当然就获取不到了。
      • 因为在别名缓存aliasMap中,存放了bean的别名student1到bean的实际名称student的映射,所以,在方法canonicalName中,就可以通过别名缓存aliasMap,根据别名student1获取bean的实际名称student,这样Spring再拿着bean的实际名称student,就可以从Spring容器中获取到bean的BeanDefinition了。

getSingleton如何通过三级缓存解决循环依赖问题

继续从doGetBean看getSingleton

在这里插入图片描述

根据方法getSingleton的名称我们可以知道,接下来应该是要获取beanName对应的单例bean了,到方法里面看下:

在这里插入图片描述

在方法getSingleton中又调用了它的重载方法getSingleton,并且在原来的参数基础上,又添加了一个新的参数allowEarlyReference,默认值为true。

在这里插入图片描述

缓存中现在是没有bean的实例的,因为我们现在是第一次调用getBean方法来实例化bean,确实,大多数情况下的bean都是在用到时,才会触发bean的实例化;

但是前面在分析ApplicationContext初始化时,如果bean中的属性lazyInit的值设置为了false,就会提前调用getBean方法触发bean的实例化,也就是提前初始化这些非延迟加载的bean,而实例化好单例对象就会被缓存在缓存singletonObjects中。

除了这种情况,前面我们还看到Spring内部的一些组件,如消息源MessageSource、事件广播器ApplicationEventMulticaster等,在Spring容器ApplicationContext初始化时就会实例化好,其实它们最后也是会注册到缓存singletonObjects中的,因为这些组件是Spring功能实现的基础,必须在Spring容器初始化时就提实例化好。

Spring中的多级缓存

首先,我们可以看到在方法getSingleton中,总共也就用到了三个缓存,分别是singletonObjects、earlySingletonObjects和singletonFactories

在这里插入图片描述

可以看到,这三个缓存其实就是三个Map,分别用来缓存不同的数据,再回到getSingleton方法

在这里插入图片描述

  • 首先会从singletonObjects中,根据bean的名称beanName来获取单例bean,singletonObjects这个缓存主要是用来存放已经完全实例化好的单例bean。

  • 再往下会有个判断:如果singletonObject为空,也就是singletonObjects中没有缓存名称为beaName的单例,并且方法isSingletonCurrentlyInCreation返回的是true,就会进入到if分支中。

    • 方法isSingletonCurrentlyInCreation中,其实就是判断集合singletonsCurrentlyInCreation中是否存在beanName。
    • 后面可以看到当bean开始实例化时,Spring就会把bean对应的名称放入到集合singletonsCurrentlyInCreation中,表示这个bean正在实例化,防止bean重复进行实例化,起到一个去重的效果。
    • 同时,当bean实例化完成后就会从集合singletonsCurrentlyInCreation中将bean的名称移除掉,这块逻辑后面我们也会看到的。
  • 如果在缓存singletonObjects中找不到beanName对应的单例bean,同时发现beanName对应的单例正在实例化,接下来就会尝试从缓存earlySingletonObjects中获取单例。

    • 其实缓存earlySingletonObjects简单来说,就是用来存放早期单例的,早期单例就是bean的实例对象还没有全部实例化完成,仅仅只是通过反射创建了一个普通的bean对象出来,bean中的很多属性都还没来得及赋值。

    • 为了满足其他地方的需要,就匆匆放到缓存earlySingletonObjects中了,所以刚才判断单例缓存中没有现成的单例bean,但是发现了bean正在实例化时,所以就在试想能不能从缓存earlySingletonObjects中,提前获取实例化到一半的bean

      • 那为什么一个bean都还没有完全实例化完成,就要匆匆放到缓存earlySingletonObjects中,暴露给外界使用呢?

      • 这还得说到Spring实例化bean过程中可能会出现的一个问题,也就是循环依赖。

      • 循环依赖:比如student1中引用了student2,student2中也引用了student1;

  • 可以看到,如果我们从缓存earlySingletonObjects中获取到的singletonObject为空,并且allowEarlyReference为true,也就是允许使用使用早期单例bean,当然,参数allowEarlyReference默认值,我们前面已经看到为true,所以条件成立;

  • 当早期单例缓存earlySingletonObjects中也还找不到单例bean时,接下来就会寻找第三个缓存也就是singletonFactories

    • singletonFactories是用来创建早期单例bean的工厂缓存,从singletonFactories中先获取到了bean的对象工厂singleFactory
    • 对象工厂singletonFactory,可以理解为是封装了早期单例bean实例化的方法,通过调singletonFactory的getObject方法,我们可以获取到早期单例bean,也就是初始化到一半的bean。
  • 接下来Spring会将创建好的早期单例bean,再放到早期单例缓存earlySingletonObjects中,因为早期单例bean现在已经获取到了,这个时候对象工厂ObjectFactory也就没有存在的意义了,就会从工厂缓存singletonFactories中移除掉,最后将得到单例singletonObject直接返回了。

简单看来,就是一层层地从这三级缓存中获取单例的bean,而Spring正是通过这三级缓存来解决循环依赖的;

Spring是如何解决循环依赖

在Spring容器中,普通bean的实例化过程至少分为三个步骤:

  1. 通过反射创建一个实例bean
  2. 然后为该实例bean填充属性
  3. 最后再为这个bean进行一些初始化操作

比如有个例子:

public class Student1 { 
    private Student2 student2; 
    
    public Student2 getStudent2() { 
        return student2; 
    } 
    public void setStudent2(Student2 student2) { 
        this.student2 = student2; 
    } 
}

public class Student2 { 
    private Student1 student1; 
    public Student1 getStudent1() { 
        return student1; 
    } 
    public void setStudent1(Student1 student1) {
        this.student1 = student1; 
    } 
}

在这里插入图片描述

  • 可以看到Student1在实例化时,首先会通过反射创建一个早期单例的bean,这个bean目前既没有设置属性值,也没有进行任何的初始化操作,也就是是一个早期的单例bean。
  • 紧接着,Student2可能此时也会进行实例化,因为Student1在实例化时,需要依赖Student2的实例为属性赋值,所以Student1的实例化是依赖Student2的实例的
    • 如果按照案例中的方式,Student1的实例在为自己的属性赋值时,就会触发Student2的实例化,而Student2在实例化到填充属性的阶段时,发现自己同样也需要依赖Student1的实例,此时就会陷入循环依赖的无限循环中了。
  • 那么首先Student2会把刚刚通过反射初步实例化好的早期单例bean,如图先封装到对象工厂ObjectFactory中,并添加到单例工厂缓存singletonFactories里,这样的话,就相当于Spring把student2初步实例化的单例bean暴露给外界了。
  • Student1此时如果要为属性赋值的话,就可以从工厂缓存中获取到Student2的早期单例bean,完成属性赋值,进而完成Student1 bean的实例化
    • Student1的实例在实例化时,当Student1的实例要为属性赋值时,就会通过工厂缓存singletonFactories,获取到Student2的早期单例的对象工厂ObjectFactory,然后通过ObjectFactory的getObject方法获取到Student2的早期单例bean。
    • 因为ObjectFactory存在的意义,就是在我们需要用到早期单例缓存bean时,才会去获取早期单例bean,此时Student2的对象工厂ObjectFactory,就可以从工厂缓存singletonFactories中移除掉了。
  • 而Student2的bean继续实例化时,发现自己为属性赋值时也需要依赖Student1的bean,这个时候可能Student1的bean已经实例化好了,所以Student2的实例化也可以完成了。
    • 当Student1的实例正常实例化完成之后,Student2的实例也会继续实例化,最后把完全实例化好的完整单例bean放到单例缓存singletonObjects中,此时,完整的单例bean就已经得到了,那就没早期单例bean什么事了,最后会把早期单例bean从早期单例缓存earlySingletonObjects中给删除掉。

需要注意的是,以上只是Spring解决setter注入循环依赖的方式,而构造方法循环依赖的问题Spring是没有办法解决的,原因也很简单的:

因为Spring费了很大的劲,才设计了这套三级缓存,通过提前暴露bean的早期单例bean,来解决setter注入循环依赖;

但是构造方法循环依赖问题,是在反射创建bean时就会发生的,此时Spring是没有办法提前获取到早期单例bean的,因为早期单例bean得要经过反射创建才能获取到,所以,对于构造方法循环依赖,Spring自然是爱莫能助了。

getObjectForBeanInstance通过FactoryBean来实例化bean

继续回到doGetBean方法

在这里插入图片描述

假设是可以从缓存中获取到单例bean的,也就是说bean的实例化过程已经经过一轮了,所以sharedInstance不为空,且参数args在doGetBean方法传进来时默认就是为空的,所以if分支条件成立,接下来就会调用方法getObjectForBeanInstance,进一步处理单例sharedInstance。

在方法getObjectForBeanInstance传进去的参数中,name是在调用getBean方法时传入的、最原始bean的名称,而beanName是name转换后bean的名称,且参数mbd默认值为null;

既然现在我们已经获取到了单例bean,那单例bean为什么不能直接拿来用呢?方法getObjectForBeanInstance还需要对单例bean做一些其他什么处理呢?

到方法getObjectForBeanInstance里面看下:

在这里插入图片描述

  • 首先会通过BeanFactoryUtls的方法isFactoryDereference,对bean原始的名称name进行判断;

    • 在这里插入图片描述

    • 也就是判断name是否以符号&"为前缀的,如果通过调用getBean方法传进来的name,如果是以符号“&"为前缀的会怎样呢?

    • 通过isFactoryDereference方法中的注释我们可以初步断定,getBean方法传入的name如果以符号"&"为前缀,目的是要获取一个工厂的引用也就是获取一个工厂bean,比如:

    • public class StudentFactoryBean implements FactoryBean
      
    • studentFactoryBean = ctx.getBean ("&student") ;
      
    • 在名称"student"前添加了符号“&"之后,直接就获取到了创建Student对象的工厂bean,也就是StudentFactoryBean对象。

  • 如果bean的名称name不是以&"为前缀的,此时就会来到下一个if分支中,这里如果判断出beanInstance不是FactoryBean的实例,直接就将beanInstance返回了。

    • 也就是说,一个bean的名称既不是以&"为前缀的,同时也不是接口FactoryBean的实现类,此时Spring就会认为这个bean没有定义相关的FactoryBean来自定义实例化bean,这个bean就是一个非常普通的bean了,就会将bean实例返回了。
    • 也就是说在默认情况下,我们是不需要根据FactoryBean来实例化bean的,只有当我们需要全权控制bean的实例化时,才需要像案例中一样自定义FactoryBean的实现类,并且在getObject方法中定义好bean的实例化的逻辑。
  • 继续往下走,那么就假设当前的bean在业务当中非常的特殊,Spring默认的实例化bean的方式已经满足不了需求了,必须得要自己来定义bean的实例化过程

    • 接下来会标记当前的mbd的类型,也就是将BeanDefinition的属性isFactoryBean值设置为true,表明当前BeanDefinition为工厂bean了。

    • 前面也看到了参数mbd的值为null,所以mbd.isFactoryBean = true的这行代码,在本次的逻辑中是不会执行的,而是执行方法getCachedObjectForFactoryBean。

    • 在这里插入图片描述

    • 可以看到,getCachedObjectForFactoryBean这里就是通过beanName到缓存factoryBeanObjectCache中获取单例,第一次来获取,缓存中当然也是没有的,所以返回结果为空。

  • 从缓存获取不到bean时,此时object为空并来到了下一个if分支,可以看到接下来果然将beanInstance强转为FactoryBean了,刚才已经知道参数mbd传进来的时候是空的,而方法containsBeanDefinition则是判断Spring容器中,是否存在beanName对应的BeanDefinition。

通过FactoryBean来实例化bean

主要看一下最后一步的getObjectFromFactoryBean方法

在这里插入图片描述

前面已经知道了,FactoryBean中的isSingleton方法返回值默认为true,且当前在单例缓存中是存在beanName对应的单例bean的,if分支成立。

可以看到会调用方法doGetObjectFromFactoryBean,进去看下:

  • 在这里插入图片描述

  • 最为关键的一行代码,也就是调用FactoryBean的getObject方法实例化bean

实例化bean的入口

还是回到doGetBean方法,可以看到在第一次调用getBean方法时,缓存中肯定是获取不到bean实例的,所以sharedInstance为空并来到else分支中。

在这里插入图片描述

首先会调用方法isPrototypeCurrentlyInCreation进行判断, 简单观察了下发现这个方法的名称,和之前分析过的方法isSingletonCurrentlyInCreation类似:

  • 在这里插入图片描述

  • 在方法isPrototypeCurrentlyInCreation中有一个集合prototypesCurrentlyInCreation,主要的逻辑就是判断下集合中是否存在beanName

  • 因为前面已经知道了方法isSingletonCurrentlyInCreation是用来判断当前在Spring中,是否存在名称为beanName的单例bean正在创建,类似的,方法isPrototypeCurrentlyInCreation则是用来判断当前Spring中,是否存在prototype类型的,且名称为beanName的bean正在创建。

  • spring创建的bean的类型默认为单例,对于单例的bean Spring在容器中仅仅只会保留一份bean的实例,而prototype类型的bean,每次调用getBean方法时都会去创建一个崭新的bean实例出来。

  • 正是因为prototype类型的bean,每次来获取时都会创建一个新的bean实例,所以Spring也就没必要为这种类型的bean进行缓存,所以prototype类型的bean自然也就没有单例bean一样的缓存待遇了。

  • 对于循环依赖问题,单例类型的bean可以通过三级缓存来解决,但是prototype类型的bean因为是没有缓存的,所以prototype类型的bean是无法解决循环依赖的,哪怕是setter循环依赖。

尝试从父类容器获取bean的实例

在这里插入图片描述

第一次过来获取bean的实例时,默认创建的是单例类型的bean,所以第一个检查可以跳过

可以看到,接下来这里还做了一个兜底的准备,通过if分支中的判断条件可以知道,如果当前Spring容器中不存在beanName对应的BeanDefinition,并且当前的Spring容器的父类容器是存在的,就会到Spring的父类容器中获取bean的实例了。

方法originalBeanName:

  • 在这里插入图片描述

  • 逻辑很简单,也就是在转换得到bean的实际名称beanName之后,再判断传进来的name是否有前缀“&”,如果有的话,就在bean的实际名称beanName前添加符号“&”。

  • 根据这个细节点,就可以意识到通过方法originalBeanName修饰过的名称,要不就是从Spring容器中获取一个普通的bean,要不就是获取一个实现了接口FactoryBean的bean实例,而不可能通过FactoryBean的getObject方法来创建对象。

在这里插入图片描述

可以看到,接下来的逻辑就容易理解的多了,就是通过父类容器去获取bean的实例了。

之前不管是在初级容器XmlBeanFactory初始化时,还是在高级容器ClassPathXmlApplicationContext初始化时,父类容器对应的参数parent的值,默认都是null,也就是Spring容器默认没有设置父类工厂,所以这块逻辑在该流程中是不会执行的。

获取并封装bean的BeanDefinition

继续往后看

在这里插入图片描述

其中参数typeCheckOnly在方法doGetBean被调用时就会传入,默认值为false,到方法markBeanAsCreated里面看下:

  • 在这里插入图片描述

  • 在方法markBeanAsCreated中,首先判断集合alreadyCreated中是否存在beanName,首次过来肯定什么东西都没有的,而且结合alreadyCreated的名称可以猜测到,alreadyCreated是用来记录已经创建了的bean的名称。

  • 而且,在方法markBeanAsCreated内部还搞了一个double check,这也是为了避免多线程并发的问题,方法中最为关键的,就是在集合alreadyCreated中添加beanName,并且清除了beanName对应BeanDefinition相关的信息,这里依旧是做一些准备相关的工作。

继续往后看:

在这里插入图片描述

关键的代码,通过方法getMergedLocalBeanDefinition的名称,可以基本知道是用来获取beanName对应的BeanDefinition,有点奇怪的是,方法getMergedLocalBeanDefinition返回了类型为RooBeanDefinition的BeanDefinition。

前面Spring在解析xml标签信息之后,Spring默认是通过GenericBeanDefinition类型的BeanDefinition来封装bean的信息的。

  • 在这里插入图片描述

  • 可以看到,方法getMergedLocalBeanDefinition中也有一个缓存mergedBeanDefinitions,它是用来存放beanName以及相应的BeanDefinition,同样的,初次到访缓存中也是什么都没有,也就是mbd为空。

  • 而在调用getMergedBeanDefinition方法的同时也调用了方法getBeanDefinition,通过传入beanName来获取Spring容器中的BeanDefinition,BeanDefinition发挥作用了,也就是通过BeanDefinition中记录的bean的一系列信息,创建一个bean实例。

  • 在方法getMergedBeanDefinition中:

    • 在这里插入图片描述

    • (1)通过创建一个RootBeanDefinition,封装从Spring容器中获取到的BeanDefinition,因为容器中的BeanDefinition是GenericBeanDefinition类型的,是没有记录父子bean标签的关系的,Spring在实例化前需要通过RootBeanDefinition来组织一下。

    • (2)如果说当前的bean配置了父类bean,那就先封装父类bean的RootBeanDefinition,否则的话,直接封装当前bean对应的BeanDefinition即可。

    • (3)将RootBeanDefinition中的类型设置为单例的,从这里就可以印证我们之前说的,也就是Spring中bean默认的类型就是单例的。

    • (4)最后,Spring会将封装好的RootBeanDefinition添加到缓存中。

提前实例化依赖的那些bean

现在bean的定义BeanDefinition已经获取到了,继续往后看

在这里插入图片描述

在方法checkMergedBeanDefinition中,只不过是判断下BeanDefinition对应的bean是否是一个抽象类的BeanDefinition,抽象类当然就不能实例化对象啊就会抛出异常

  • 在这里插入图片描述

继续会通过getDependsOn方法获取属性dependsOn的值。那么BeanDefinition中的dependsOn是什么:

  • <?xml version="1.0" encoding="UTF-8"?> 
    <beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans.xsd">    
        <bean id="student1" class="com.ruyuan.container.cycle.setter.Student1"/> 
        <bean id="student2" class="com.ruyuan.container.cycle.setter.Student2"            depends-on="student1"/> </beans>
    
  • 比如,我们当前要实例化的bean是student2,而student2标签中配置了属性depends-on的值为student1,表示student2的这个bean在实例化时,需要依赖student1这个bean。

  • 所以在实例化student2之前,Spring会先实例化student1这个bean,其中,BeanDefinition中的getDependsOn方法获取的值,就是标签中的属性depends-on的值student1。

继续可以看到会遍历bean依赖的所有bean的名称dep,然后if判断调用isDependent方法:

  • 在这里插入图片描述

  • 首先会从dependentBeanMap中获取beanName对应的集合dependentBeans。

  • dependentBeans其实就是beanName依赖的那些bean名称的集合,统一都被缓存在了集合dependentBeanMap中,接下来会判断下当前bean依赖的dependentBeanName,是否已经存在于beanName依赖的集合dependentBeans中。

    • 举个例子,假如当前的beanName依赖的bean的集合元素为:beanName1、beanName2和beanName3,它们存放于集合dependentBeans中,如果dependentBeanName的名称为beanName1,此时就和集合dependentBeans中的元素重复了,所以就直接返回true了,这是第一种情况。
  • dependentBeanName除了会被检查是否在beanName依赖的范围之内,还会进一步的进行深度检查,会遍历beanName依赖的那些bean的名称dependentBeans

    • 假设beanName依赖bean的名称为beanName1、beanName2和beanName3,假如beanName1依赖的bean集合元素为:beanName11、beanName12和beanName13。
    • 此时,我们假设dependentBeanName的值为beanName11,首先就可以避开beanName直接依赖集合的检查,代码会执行到for循环这里,此时,在第一轮遍历时就会发现dependentBeanName的名称,与beanName1依赖集合中的beanName11重复了,同样也会返回true。
  • 所以只要传进来受检查的dependentBeanName位于beanName bean的依赖链上,就会返回true,表示beanName的依赖链上已经存在这个依赖了,当前bean配置的依赖就是不合理的,相当于也是一种循环依赖的判断。

继续向下看,isDependent方法返回结果为true时,就会抛异常中断这个操作。

如果bean配置的依赖都通过了检查,接着会调用方法registerDependentBean:

  • 在这里插入图片描述

  • 在方法registerDependentBean中,会将bean的名称以及和它依赖的那些bean的名称,都注册到相应的的缓存中。

  • 而且,正是因为dependsOn是bean依赖的那些bean,所以在实例化bean之前,Spring会提前调用getBean方法来实例化依赖的这些bean。

bean的实例化

还是回到doGetBean方法,在经过尝试从父类容器获取bean实例、获取并封装bean的BeanDefinition、提前实例化依赖的那些bean之后,就会来到创建单例bean的入口了:

在这里插入图片描述

首先会判断BeanDefinition中的bean类型是否为单例的, 再通过方法getSingleton获取单例bean的实例sharedInstance,然后进一步通过方法getObjectForBeanInstance,检查下当前的bean是否需要通过FactoryBean来创建。

这里的getSingleton方法就不是从缓存中获取bean的实例了,而是从零开始创建一个单例bean,可以看到在调用方法getSingleton时,除了将bean的名称beanName传进去之外,还添加了一个匿名内部类作为参数,而且在匿名内部类中有一个方法createBean。

根据方法的名称,初步可以判定在createBean方法中,应该就包含了实例化bean的核心逻辑了

在这里插入图片描述

而且可以看到传入的匿名内部类是ObjectFactory类型的参数singletonFactory

  • 在这里插入图片描述

  • 可以看到ObjectFactory其实就是一个接口,接口中只提供了一个方法getObject,Spring默认会通过匿名内部类的方式来实现getObject方法,在getObject方法中添加了createBean方法来实例化bean。

  • 通过实现ObjectFactory中的getObject方法来实例化bean,就是使用了普通工厂设计模式

  • 而且,虽然ObjectFactory和FactoryBean,在名称上来看确实还挺像的,但是实际上的差异还是很大的,如果一个bean没有实现FactoryBean接口,Spring默认就会通过ObjectFactory中封装的这套逻辑来实例化bean。

  • 但是,如果说bean实现了FactoryBean接口,那Spring虽然默认会通过ObjectFactory中的逻辑先实例化一个bean,但是最终得到的单例bean的实例,还是会以FactoryBean中getObject方法获得的单例bean实例为准

从全局视角看bean的实例化

继续来看下getSingleton方法:

在这里插入图片描述

  • 首先,Spring在实例化bean之前,会调用方法beforeSingletonCreation记录当前的beanName对应的bean正在创建
  • 然后,Spring会调用singletonFactory中的getObject方法来实例化bean。
  • 而在bean实例化之后,Spring又会调用afterSingletonCreation方法,来记录当前的bean已经创建好了,并且将创建好的单例bean通过addSingleton方法添加到相应的缓存中。

预先处理需要覆盖的方法

还是先回到调用getSingleton方法的位置

在这里插入图片描述

因为 ObjectFactory类中getObject方法中的逻辑,如图所示,其实就是在匿名内部类中封装的,而且刚才也看到了,Spring默认就是通过这部分的逻辑来实例化单例bean的,所以,关于单例bean的实例化,一切的一切都是围绕着这部分代码来展开的。

可以看到在匿名内部类中,首先会通过方法createBean来实例化一个bean,如果实例化的过程当中出现异常,就会调用方法destroySingleton方法,来清除单例bean相关的一系列缓存信息。

先从createBean方法开始分析:

在这里插入图片描述

  • 在createBean方法中,依然还是在做一些bean实例化前的准备工作:
  • 比如,在resolveBeanClass方法中先获取单例bean的Class对象,然后调用mbdToUse也就是BeanDefinition的prepareMethodOverrides方法,提前标记下需要覆盖的方法。
  • 接下来会调用方法resolveBeforeInstantiation获取bean的实例,而且,如果bean不为空直接就返回了。
    • 通过注释,我们可以知道方法resolveBeforeInstantiation是Spring提供给bean后处理器BeanPostProcessor来创建一个代理对象的机会。
  • 接下来会调用方法doCreateBean,可以确定Spring默认实例化bean的逻辑,就在doCreateBean方法中。

通过反射来实例化bean

仔细分析一下上面的doCreateBean方法

在这里插入图片描述

首先,如果发现BeanDefinition为单例类型,就会从factoryBeanInstanceCache中获取一个BeanWrapper,默认BeanDefinition的类型为单例且factoryBeanInstanceCache是为空的,所以,获取到的instanceWrapper最终也是为null。

其中,factoryBeanInstanceCache就是一个缓存,是用来存放FactoryBean实例对应的BeanWrapper,而BeanWrapper可以理解为就是bean实例的一个包装类而已,再得到bean的最终实例之前,bean的实例化还得要依附于BeanWrapper;

接下来,如果从缓存factoryBeanInstanceCache中获取不到instanceWrapper之后,Spring就会调用createBeanInstance方法来创建一个BeanWrapper:

  • 在这里插入图片描述

  • 首先会调用方法resolveBeanClass,从BeanDefinition mbd中解析并获取Class,前面已经调用过一次方法resolveBeanClass,并成功获取过一次bean的Class了,而Spring之所以在这里重复调用一次该方法,只不过是想要确认bean的Class一定是能解析成功的。

  • 而且在第一个if分支中,Spring会判断bean对应的Class如果不是被public关键字修饰的,并且也没有通过反射机制来设置这个Class是可以访问的,就会抛出异常中止bean的实例化。

  • 方法后续的逻辑,其实就是一些和反射API相关的的操作了

  • 接下来可以看到会通过方法getFactoryMethodName,判断下bean对应的BeanDefinition中,是否存在属性factoryMethodName的值

    • 其中,getFactoryMethodName方法获取的,就是bean标签中factory-method属性配置的工厂方法名称,如果getFactoryMethodName方法的返回值不为null,就会调用工厂方法来实例化bean了,就不会走Spring的默认实例化bean的逻辑了,这里同样也是可以利用的一个扩展点。
  • 而接下来的方法instantiateUsingFactoryMethod中的逻辑,本质上就是一些反射相关的操作

  • 通过反射实例化一个bean

    • 在这里插入图片描述

    • 初次实例化bean时,BeanDefinition中的resolvedConstructorOrFactoryMethod属性默认是为空的,所以该部分的代码初次是不会执行的,再看到下面这部分的代码:

    • 在这里插入图片描述

    • 主要是通过determineConstructorsFromBeanPostProcessors方法,执行一些后处理器BeanPostProcessor中的逻辑,而这些后处理器中的逻辑,主要是获取Class中的多个重载构造方法ctors。

      • 在if分支中,其中有一个判断逻辑为 mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR,和@Autowired注解有关系
      • if分支里面的方法autowireConstructor,主要是为了实例化构造方法上添加了@Autowired注解的bean
    • 在当前要实例化的bean中,确实也是没有其他重载的构造方法,所以,接下来代码就会来到最后一个方法instantiateBean中:

      • 在这里插入图片描述

      • 首先会通过方法instantiate实例化一个实例beanInstance,然后将bean的实例封装到BeanWrapper中

      • 方法instantiate:

      • 在这里插入图片描述

      • 核心方法BeanUtils.instantiateClass:

      • 在这里插入图片描述

为刚刚实例化的bean填充属性

还是再回到doCreateBean方法

现在我们通过方法createBeanInstance得到实例化好的bean实例了,并且bean实例目前是包装instanceWrapper中。

在这里插入图片描述
因为BeanDefinition中postProcessed属性值默认是false,所以,接下来会来到if分支中调用方法applyMergedBeanDefinitionPostProcessors:

  • 在这里插入图片描述

  • 方法中主要就是遍历并且执行所有MergedBeanDefinitionPostProcessor中的postProcessMergedBeanDefinition方法。

  • 其中,MergedBeanDefinitionPostProcessor是继承BeanPostProcessor的,所以,MergedBeanDefinitionPostProcessor也是一个后处理器,其实,MergedBeanDefinitionPostProcessor的主要职责,就是用来合并一些信息到BeanDefinition中的,尤其是bean的属性上添加了注解@Autoired、@Resource等,这些注解上面的信息当然也是需要被纳入到BeanDefinition中的。

继续往下走:

在这里插入图片描述

Spring就是在这里,将封装好了早期单例bean的ObjectFactory,添加到了三级缓存也就是单例工厂缓存singletonFactories中。

接下来会调用方法populateBean

在这里插入图片描述

方法populateBean从字面上来看就是填充bean的意思,说白了就是为刚实例化好的bean填充各种属性的值。

但是,bean属性相关的信息都存放在BeanDefinition中,毕竟BeanDefinition是bean的定义,存放了关于bean的几乎一切信息,所以,接下来Spring会调用方法populateBean获取BeanDefintion中的属性信息,然后填充到bean的实例当中:

  • 在这里插入图片描述

  • 参数bw当前是不为空的,所以第一个if分支暂时可以跳过

  • 在第二个if分支中,Spring可以通过调用后处理器InstantiationAwareBeanPostProcessor中的方法postProcessBeforeInstantiation来实例化bean,当然,如果方法postProcessBeforeInstantiation中没有定义实例化bean的逻辑,那在另外一个方法postProcessAfterInstantiation中,可能还是会有一些逻辑的。

  • 只不过现在,Spring已经默认实例化了一个bean出来了,所以,此时调用方法postProcessAfterInstantiation,最多也只是Spring来为刚实例化好的bean,设置一些属性的值而已,而且默认情况下,InstantiationAwareBeanPostProcessor的一些实现类其实都是一些空实现,默认都是返回true的,依然可以理解为这是Spring提供的扩展点而已。

  • 然后会调用mbd也就是BeanDefinition中的getResolvedAutowireMode方法,获取bean的自动装配模式:

    • 在这里插入图片描述

    • 其中,成员变量this.autowireMode表示当前BeanDefinition的自动装配模式,在getResolvedAutowireMode方法中,如果设置了属性autowireMode的值为AUTOWIRE_AUTODETECT,就表示由Spring自动探测当前bean到底该使用哪种装配置模式。

    • 探测的方法也很简单,可以看到Spring会获取Class中的所有构造方法constructors,并且判断是否存在参数个数为0的构造方法,也就是无参构造方法是否存在,如果存在,Spring就会决定使用AUTOWIRE_BY_TYPE的模式去自动装配bean属性的值,否则,Spring就会使用AUTOWIRE_CONSTRUCTOR的模式去自动装配属性的值。

    • 默认情况下,成员变量this.autowireMode的值为AUTOWIRE_NO,表示Spring在xml文件中默认是关闭自动装配功能的。

  • 然后到方法autowireByName, Spring根据名称来自动装配

  • 方法autowireByType,Spring对属性的类型进行自动装配

  • 根据名称和类型进行自动装配之后, 依然是在处理后处理器InstantiationAwareBeanPostProcessor,只不过这里是调用方法postProcessProperties,提供一些属性信息。InstantiationAwareBeanPostProcessor在这里调用方法postProcessProperties,主要还是用来获取注解上的一些属性信息,如@Autowired、@Resource等

  • Spring最后会将前面解析到的所有属性信息,通过方法applyPropertyValues填充到bean实例中,因为当前bean实例还包裹在BeanWrapper中,所以暂时还是通过BeanWrapper来为bean实例设置属性值的。

bean是如何进行初始化的

还是再回到doCreateBean方法

在这里插入图片描述

populateBean为bean设置各种属性之后,就到方法initializeBean了,要初始化bean的实例了:

  • 在这里插入图片描述

  • 在方法initializeBean中,首先会执行方法invokeAwareMethods

    • 方法invokeAwareMethods也就是说Spring在bean实例化时,最终会调用感知接口中的方法,将Spring容器内相应的组件注入到bean的实例中。
  • 然后方法applyBeanPostProcessorsBeforeInitialization和applyBeanPostProcessorsAfterInitialization:

    • 在这里插入图片描述

    • 这两个方法是在执行后处理器BeanPostProcessor相关的逻辑,这里执行的是最底层接口BeanPostProcessor中的前置和后置处理方法。

  • 方法invokeInitMethods应该是初始化bean的核心了:

    • 在这里插入图片描述

    • 方法invokeInitMethods首先会判断当前bean,是否是接口InitializingBean的实例,如果是的话,就会执行InitializingBean中的方法afterPropertiesSet。

      • 方法afterPropertiesSet的调用时机,是在bean实例完成所有属性值设置之后,而且,方法afterPropertiesSet的作用,主要是对bean实例进行一些配置和最终的初始化操作。
    • 接下来会通过方法getInitMethodName,从mbd也就是bean的BeanDefinition中获取bean的初始化方法。

    • 然后会执行定制化的初始化方法

再回到doCreateBean方法:

在这里插入图片描述

Spring默认是允许曝光早期单例bean的,所以earlySingletonExposure为true,可以看到,接下来会先调用方法getSingleton,从缓存中获取单例bean。

因为参数allowEarlyReference值为false,表示不允许早期引用,也就是不允许从工厂缓存获取早期的单例bean,所以,方法getSingleton初次被调用时,得到的earlySingletonReference为null。

继续走:

在这里插入图片描述

接下来,Spring会注册销毁bean相关的方法, 方法registerDisposableBeanIfNecessary主要就是为bean注册DisposableBeanAdapter,而DisposableBeanAdapter就是在bean销毁时发挥作用的。

DisposableBeanAdapter默认实现接口DisposableBean,接口DisposableBean中只有一个方法destory,方法destory会在bean销毁之前被Spring容器调用执行。

通过接口DisposableBean中的方法destory,可以在bean销毁之前做一些事情,比如可以在destory方法中进行垃圾回收,或者释放和bean相关的一些资源等。

截止到这里,bean加载的逻辑基本上都已经分析完毕,再回到AbstractBeanFactory#doGetBean方法单例bean创建的位置来看下:

在这里插入图片描述

当单例bean实例化之后,和之前从缓存中获取单例bean类似,接下来还会调用方法getObjectForBeanInstance,看下bean是否是FactoryBean的实例,并且是否需要通过FactoryBean来实例化bean

最后,就是对单例之外的其他类型bean的实例化了,比如prototype等

其他类型bean的实例化和单例bean的实例化,在主流程上都比较类似的,可以看到,Spring最后会将实例化好的bean直接返回,截止到这里,bean的加载流程算是结束了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值