spring容器核心原理--bean的存储以及Bean的三级缓存

本文详细探讨了Spring容器中bean的存储和三级缓存机制。bean对象在转换为beanDefinition后存储在beanDefinitionMap中,允许在初始化前进行扩展。在DefaultSingletonBeanRegistry中,bean的三级缓存包括singletonObjects、earlySingletonObjects和singletonFactory。创建bean分为实例化、属性赋值和初始化三个步骤,每个阶段都有相应的缓存策略。三级缓存在不同时机发挥作用,确保高效管理和获取bean。
摘要由CSDN通过智能技术生成

一.bean的存储

通常我们说,spring容器帮我们管理bean,但是什么是bean?
举个简单的例子,如果我们自己提供一个X.class类,那此时的X.class我们称之为class对象,在经过spring的一系列生命周期的处理之后,就会变成可以放到spring容器中的bean对象,我们称此时的对象是bean对象,所以,总结来说,经过一系列生命周期方法处理之后的class对象,就是bean对象;
只有经过一系列后置处理器方法的处理,才是一个合格的bean对象。

bean对象最终存储在spring容器中,我们简单的、狭义上的spring容器,在spring源码底层就是一个map集合,这个map集合存储的key是当前bean的name,如果不指定,默认的是class类型首字母小写作为key,value是bean对象。存储bean的map在DefaultListableBeanFactory类中:

/** Map of bean definition objects, keyed by bean name. */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

当Spring容器扫描到Bean类时 , 会把这个类的描述信息, 以包名加类名的方式存到beanDefinitionMap 中,
Map<String,BeanDefinition> , 其中 String是Key , 默认是类名首字母小写 , BeanDefinition , 存的是类的定义(描述信息) , 我们通常叫BeanDefinition接口为 : bean的定义对象。

问题思考

spring为什么不在扫描到class文件之后,立即执行生命周期方法进行初始化、实例化?而是要先放到beanDefinitionMap集合中?
因为spring所提供的容器管理功能中,某些class类并不一定是立马需要初始化的,比如:原型bean,就是在使用的时候,再去初始化。

那我们想下,如果是原型bean,并且假设我们没有beanDefinition这一层,那问题就大了,对于原型的bean,我每初始化一次,就需要去扫描一次,这样就很浪费时间了,此时,beanDefinition的作用,就显而易见了。提一嘴,spring容器在启动的过程中,对bean进行初始化实例化的时候,大致是分为了两步,第一是将class文件转换为beanDefinition对象,第二步是根据beanDefinition对象,按照配置的要求,去进行初始化、实例化
有了beanDefinition之后,beanDefinitionMap相当于一个中转站,所有要初始化的bean,都是以beanDefinitionMap中的beanDefinition的属性信息为准。

1.spring在将class文件转换为beanDefinition的时候,是没有额外的要求的,不管这个bean是单例的,还是原型的,还是懒加载的,都会扫描出来,统一放到beanDefinitionMap集合中。
2.在第二步去初始化bean的时候,才会判断当前bean是否是原型的、是否是懒加载的、是否是有依赖关系的,等

所以,我们也可以认为beanDefinition是一个中转站,这个中转站中,存放了所有的class对应的信息,如果是初始化,就根据beanDefinition的属性信息去初始化,所以,spring也提供了一系列的扩展点让我们去修改beanDefinition、增加、删除beanDefinition。
所以,简单来说,我们自己写得一个class文件,在放到spring容器中,是这样的一个步骤:

A.class --> beanDefinition --> bean

所以,中间加了一层beanDefinition之后,那很有可能就会出现,我们提供的是X.class,但是在将class存入到beanDefinition之后,将对应的beanDefinition属性的beanClass设置为了Y.class,那这时候初始化的就是Y对象,而不是X对象。
这只是举个例子,想要说明的是:我们提供了一个X.class,正常情况下,初始化的就是x这个bean,但是在特殊情况下,我们可以将X的beanDefinition的beanClass修改为Y.class,这样就偷天换日了,实际初始化的是Y对象;这个过程就依赖于Spring所提供的N多个扩展点。
在将class放入到beanDefinitionMap之后,可以通过扩展点去修改beanDefinition的属性信息。

所以,我们提供的X类,最后并不一定初始化的就是X这个bean,许多底层框架,也是通过beanDefinition的扩展点去完成自己的业务逻辑的。

如果我们想在Spring对class初始化的过程中,进行干预怎么办?
Spring提供了一系列的扩展点,让我们根据自己的业务需求,去处理class文件从class变成bean对象过程中的关键点。我觉得,这也是Spring强大的地方,不仅仅帮我们去管理bean,也提供了相应的扩展点,让我们根据自己的需求去扩展Spring,比如:beanFactoryPostProcessor可以让我们去干预beanDefinitionMap中的beanDefinition对象;spring的后置处理器,可以让我们根据自己的需求,去干预各个后置处理器方法执行的逻辑。

二.Bean的三级缓存

在DefaultSingletonBeanRegistry类中为Bean的创建设置了三级缓存:

第一级缓存:singletonObjects
第二级缓存:earlySingletonObjects
第三级缓存:singletonFactory

/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

完成一个Bean对象的创建需要三步

1.创建Bean实例(createBeanInstance)
-这里主要操作是从之前已经处理好的BeanDefition(Bean定义信息)中实例化一个对象。

2.Bean对象属性赋值(populateBean)
-像@Autowired注解修饰的成员变量是在这一步完成注入的。

3.Bean对象的初始化(初始化Bean实例)
-这一步会去调用这个Bean继承了某些接口(BeanNameAware等)的方法。

在AbstractAutowireCapableBeanFactory类中创建Bean实例:

/**
 * Actually create the specified bean. Pre-creation processing has already happened
 * at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.
 * <p>Differentiates between default bean instantiation, use of a
 * factory method, and autowiring a constructor.
 * @param beanName the name of the bean
 * @param mbd the merged bean definition for the bean
 * @param args explicit arguments to use for constructor or factory method invocation
 * @return a new instance of the bean
 * @throws BeanCreationException if the bean could not be created
 * @see #instantiateBean
 * @see #instantiateUsingFactoryMethod
 * @see #autowireConstructor
 */
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
		throws 
Spring框架中,Bean的缓存是通过三级缓存实现的。其中级缓存就是指单例Bean的缓存。 当我们首次通过Spring容器获取一个单例Bean时,Spring会先从一级缓存中查找是否已经存在该Bean的实例。如果存在,则直接返回该实例;如果不存在,则继续执行后续步骤。 在后续的步骤中,Spring会尝试从二级缓存中获取Bean的实例。二级缓存是一个用于存储原始的Bean定义信息的缓存,它是为了解决循环依赖问题而存在的。如果二级缓存中存在Bean的定义信息,则Spring会根据这些信息创建一个新的Bean实例,并放入一级缓存中,然后返回该实例。 如果二级缓存中不存在Bean的定义信息,或者在创建Bean实例时出现了循环依赖问题,那么Spring会继续执行后续步骤。 在后续步骤中,Spring会尝试从三级缓存中获取Bean的实例。三级缓存是一个用于存储已经创建但是尚未初始化完成的Bean实例的缓存。如果三级缓存中存在Bean的实例,则Spring会使用这个实例,并将其放入一级缓存中,然后返回该实例。 如果三级缓存中不存在Bean的实例,或者在初始化Bean实例时出现了异常,那么Spring会继续执行后续步骤。 在后续步骤中,Spring会根据Bean的定义信息创建一个新的实例,并将其放入一级缓存中,然后返回该实例。 总结一下,一级缓存就是用于缓存单例Bean实例的,它是SpringBean缓存机制的第一层。一级缓存的存在可以提高Bean的获取效率,避免重复创建实例。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值