内容来自于 知乎文章
Spring加载bean的流程
- 首先根据配置文件或者启动类把左右bean注册成bean定义。bean定义就是用于映射bean和java类的一个标签。
- 遍历所有bean定义的beanName,通过调用 **BeanFactory的getBean(beanName)**方法,创建、初始化并返回bean实例。
三级缓存主要关注getBean方法中具体的流程
GetBean()方法的流程
- 依次从一级-三级缓存中尝试获取bean。
- 对于每一级缓存,如果没有bean:
2.1先把beanName标记为正在创建。
2.2通过class获取构造方法通过反射创建实例。
2.3如果beanName状态为正在创建,则把它的工厂对象放入三级缓存。 - 初始化实例对象、移除正在创建标记、将实例放入一层缓存、清理二级、三级缓存的内容、返回实例。
三级缓存和代理的关系?
其实,如果要打破循环依赖,直接把实例化的对象放到二级缓存中,发现依赖时直接去二级缓存中取就能解决了,但是为什么要再弄个三级缓存放bean的工厂对象呢?
一级缓存
用了一个ConcurrentHashMap,线程安全的哈希表。
首先,先确定一级缓存的重要性,所有的能供我们用的bean都在这里,项目运行时,获取bean都是从这里获取的。
二级缓存
二级缓存叫做 earlySingletonObjects,存放实例化但是没初始化的bean。
三级缓存
从泛型能看出,三级缓存这个map存的是一个ObjectFactory,它有一个方法叫做 getEarlyBeanReference(),用于生成实例化但没初始化的对象,用于放入二级缓存。
如果这个bean有初始化的后置逻辑,需要遍历它所有的后置处理processor。通过另一个getEarlyBeanReference方法获取到一个bean,然后返回这个bean。这是在干什么?
我们去看下这个getEarlyBeanReference()的方法:在一个创建代理的类中实现的:
这里最终返回的是,wrapIfNecessary()的结果,也就是一个代理对象。因此可以得出结论:如果bean有后置逻辑,需要代理时,三计缓存中工厂获取bean的逻辑中,会返回bean的代理对象而不是bean对象。
并且要保证如果其余地方有引用当前bean时,获取的时同一个引用,因此会将这里的bean从工厂中拿出后立刻放入二级缓存。(下次来直接取二级缓存中的,就不会出发工厂的getBean方法了)