Spring的Bean加载和缓存

270 阅读3分钟

回顾

  • 上回说到Spring会根据配置文件解析出Bean的元数据信息,将其抽象为BeanDefinition。
  • BeanDefinition并不是Bean的实例。
    • BeanDefinition里有Bean的Class信息,Scope,Role。。。
    • image.png
  • 工厂里有一个ConcurrentHashMap,为beanName到beanDefinition的映射。

本篇收获

  • Bean是如何实例化的。
  • 单例Bean是如何缓存的,存在哪。
  • 也就是获取Bean实例时使用的factory.getBean,做了什么。

把之前写的代码捞出来

public class SpringClient {
    public static void main(String[] args) {
        Resource resource = new ClassPathResource("applicationContext.xml");
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        BeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(factory);
        beanDefinitionReader.loadBeanDefinitions(resource);

        Student student = (Student) factory.getBean("student");
        System.out.println(student.getName() +"\n" + student.getAge());
    }
}

doGetBean()方法

一路跟进getBean方法

  • 会发现一个getSingleton方法image.png
  • 看一看getSingleton方法image.png
  • 发现里面有个成员变量singletonObjects,看一看是啥。image.png
  • 单例对象,也是缓存在工厂的ConcurrentHashMap里。
  • 有了Class就可以通过反射来实例化Bean了。
    • 跟进代码会有一个doCreateBean方法,里面有 image.png
  • 当然肯定不仅仅是实例化Bean,Spring在帮你实例化的过程中肯定有帮你完成一些检查,比如循环依赖,判断类是不是public,是不是接口,构造方法的参数是否匹配,scope是singleton还是prototype,以及在创建bean的过程中的并发问题,等等操作(debug一下就能发现)。

给Bean的成员变量赋值

ctor.newInstance只是创建了含有默认值的Bean实例

image.png

给Bean的成员变量赋值关键点

image.png

  • 填充Bean,简单说就是给Bean的成员变量赋值。
  • 暴露Bean,简单说就是让这个Bean,可以被其他人感知(Aware)到这个Bean,以及执行自定义的初始化方法,以及调用后置处理器(post processors)。

给Bean成员变量赋值的关键代码

  • QQ截图20220828144558.png
  • 顺便一提,对Bean成员变量的描述和访问Spring也进行了抽象。 image.png
  • 给变量赋值的最终操作,肯定是通过反射,拿到Bean的set方法,然后调用它,以完成赋值。 image.png

将Bean缓存

scope是单例才会缓存,如果scope是prototype,就不会缓存了,但创建Bean的流程基本是一致的。

  • 创建完Bean。
    image.png
  • singletonObject创建完了。可以缓存了。 image.png

小结

关于Spring Bean的创建流程

  1. Spring所管理的Bean实际上是缓存在一个ConcurrentHashMap中的(singletonObjects对象中)。该对象本质上是一个key-value对的形式,key指的是bean的名字(id) ,value是一个object对象,就是所创建bean的实例对象。
  2. 在创建Bean之前,首先需要将该Bean的创建标识指定好markBeanAsCreated(String beanName) ,表示该bean已经或是即将被创建,目的是增强缓存的效率。
  3. 根据bean的scope属性来确定当前这个bean是一个singleton还是一个prototype的bean,然后创建相应的对象。
  4. 无论是singleton还是prototype的bean,其创建的过程是一致的
  5. 通过Java反射机制来创建Bean的实例,在创建之前需要检查构造方法的访问修饰符,如果不是public的,则会调用setAccessible(true)方法来突破Java的语法限制,使得可以通过非public构造方法来完成对象实例的创建。
    • 就算构造方法是private,我也可以通过反射来实例化对象。
  6. 当对象创建完毕后,开始进行对象属性的注入。
  7. 在对象属性注入的过程中,Spring除去使用之前通过BeanDefinition对象获取的Bean信息外,还会通过反射的方式获取到上面所创建的Bean中的真实属性信息(还包括一个class属性,表示该Bean所对应的class类型)。
  8. 完成Bean属性的注入(或者抛出异常)
    • 比如成员属性的getter和setter方法不一致。例如,属性是name,但却是setName1。就会抛异常。
  9. 如果Bean是一个单例的,那么将所创建出来的Bean添加到singletonObjects对象中(缓存中),供程序后续再次使用。