java面试题Spring部分(四)
创始人
2024-11-12 07:09:09

一、什么事spring的三级缓存

典型回答

在Spring的BeanFactory体系中,BeanFactory是Spring IOC容器的基础接口,其DefaultSingletonBeanRegistry类实现了BeanFactory接口,并维护了三级缓存:

public class DefaultSingletonBeanRegistyr extends SimpleAliasRegistry implements SingletonBeanRegistry {   //一级缓存,保存完成的Bean对象   private final Map singletonObjects = new ConcurrentHashMap<>(256);   //三级缓存,保存单例Bean的创建工厂   private final Map> singletonFactories = new HashMap<>(16);   //二级缓存,存储"半成品"的Bean对象   private final Map earlySingletonObjects = new ConcurrentHashMap<>(16); } 

**singletonObjects是一级缓存,存储的是完整创建好的单例bean对象。**在创建一个单例bean时,会先从singletonObjects中尝试获取该bean的实例,如果能够获取到,则直接返回该实例,否则继续创建该bean。

**earlySingletonObjects是二级缓存,存储的是尚未完全创建好的单例bean对象。**在创建单例bean时,如果发现该bean存在循环依赖,则会先创建该bean的"半成品"对象,并将"半成品"对象存储到earlySingletonObjects中。当循环依赖的bean创建完成后,Spring会将完整的bean实例对象存储到singletonObjects中,并将earlySingletonObjects中存储的代理对象替换为完整的bean实例对象。这样可以保证单例bean的创建过程不会出现循环依赖问题。

**singletonFactories是三级缓存,存储的是单例bean的创建工厂。**当一个单例bean被创建时,Spring会先将该bean的创建工厂存储到singletonFactories中,然后再执行创建工厂的getObject()方法,生成该bean的实例对象。在该bean被其他bean引用时,Spring会从singletonFactories中获取该bean的创建工厂,创建出该bean的实例对象,并将该bean的实例对象存储到singletonObjects中。

二、Spring解决循环依赖一定需要三级缓存吗

典型回答

使用二级缓存也能解决循环依赖问题,但是如果完全依赖二级缓存解决循环依赖,意味着当我们依赖了一个代理类的时候,就需要在Bean实例化之后完成AOP代理,而在Spring的设计中,为了了解Bean的初始化和代理,是通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来在Bean生命周期的最后一步来完成AOP代理的。
但是,在Spring的初始化过程中,他是不知道哪些Bean可能有循环依赖的,那么,这时候Spring面临两个选择:

  1. 不管有没有循环依赖,都提前把代理对象创建出来,并将代理对象缓存起来,出现循环依赖时,其他对象直接就可以取到代理对象并注入。
  2. 不提前创建代理对象,在出现循环依赖时,再生成代理对象。这样在没有循环依赖的情况下,Bean就可以按着Spring设计原则的步骤来创建。

第一个方案看上去比较简单,只需要二级缓存就可以了。但是他也意味着,Spring需要在所有的bean的创建过程中就要先成代理对象再初始化;那么这就和spring的aop的设计原则(前文提到的:在Spring的设计中,为了解耦Bean的初始化和代理,是通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来在Bean生命周期的最后一步来完成AOP代理的)是相悖的。

而Spring为了不破坏AOP的代理设计原则,则引入第三级缓存,在三级缓存中保存对象工厂,因为通过对象工厂我们可以在想要创建对象的时候直接获取对象。有了它,在后续发生循环依赖时,如果依赖的Bean被AOP代理,那么通过这个工厂获取到的就是代理后的对象,如果没有被AOP代理,那么这个工厂获取到的就是实例化的真实对象。

三、三级缓存是如何解决循环依赖问题的

典型回答

**Spring中Bean的创建过程其实可以分为两步:第一步实例化,第二步初始化
实例化的过程只需要调用构造函数把对象创建出来并给他分配空间,而初始化则是给对象的属性进行赋值。
而Spring之所以可以解决循环依赖就是因为对象的初始化是可以延后的,也就是说,当我创建一个Bean Service的时候,会先把这个对象实例化出来,然后再初始化其中的serviceB属性。

@Service public class Service{  	@Autowired 	private ServiceB serviceB; }  @Service public class ServiceB{  	@Autowired 	private ServiceA serviceA; } 

而 当一个对象只进行了实例化,但是还没有进行初始化时,我们称之为半成品对象。所以,所谓半成品对象,其实就是bean对象的空壳子,还没有进行属性注入和初始化。

当两个Bean在初始化过程中互相依赖的时候,如初始化A发现依赖了B,继续去初始化B,发现又依赖了A。大致流程如下图:
在这里插入图片描述
以上方法,就是通过引入三级缓存,解决了循环依赖的问题,在上述流程执行完成之后,ServiceA和ServiceB都被成功的完成了实例化和初始化。
以下是DefaultSingletonBeanRegistry#getSingleton方法,代码中,包括一级缓存、二级缓存、三级缓存的处理逻辑,该方法是获取bean的单例实例对象的核心方法:

@Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) {     // 首先从一级缓存中获取bean实例对象,如果已经存在,则直接返回     Object singletonObject = this.singletonObjects.get(beanName);     if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {         // 如果一级缓存中不存在bean实例对象,而且当前bean正在创建中,则从二级缓存中获取bean实例对象         singletonObject = this.earlySingletonObjects.get(beanName);         if (singletonObject == null && allowEarlyReference) {             // 如果二级缓存中也不存在bean实例对象,并且允许提前引用,则需要在锁定一级缓存之前,             // 先锁定二级缓存,然后再进行一系列处理             synchronized (this.singletonObjects) {                 // 进行一系列安全检查后,再次从一级缓存和二级缓存中获取bean实例对象                 singletonObject = this.singletonObjects.get(beanName);                 if (singletonObject == null) {                     singletonObject = this.earlySingletonObjects.get(beanName);                     if (singletonObject == null) {                         // 如果二级缓存中也不存在bean实例对象,则从三级缓存中获取bean的ObjectFactory,并创建bean实例对象                         ObjectFactory singletonFactory = this.singletonFactories.get(beanName);                         if (singletonFactory != null) {                             singletonObject = singletonFactory.getObject();                             // 将创建好的bean实例对象存储到二级缓存中                             this.earlySingletonObjects.put(beanName, singletonObject);                             // 从三级缓存中移除bean的ObjectFactory                             this.singletonFactories.remove(beanName);                         }                     }                 }             }         }     }     return singletonObject; } 

相关内容

热门资讯

裸辞做“一人公司”,我后悔了 去年这个时候,一位以色列程序员正在东南亚旅行。他顺手把一个在脑子里转了很久的想法做成了产品,一个让任...
南京建成国内首个Pre-6G试... 4月21日,2026全球6G技术与产业生态大会在南京开幕。全息互动技术展台前,一名远在北京的工作人员...
超梵求职受邀参加“2025抖音... 超梵求职受邀参加“2025抖音巨量引擎成人教育行业生态大会”,探讨分享优质内容传播,服务万千学员。 ...
摩托罗拉Razr 2026(R... IT之家 4 月 22 日消息,摩托罗拉宣布新一代 Razr 折叠手机将于 4 月 29 日在美国发...
库克卸任,特纳斯领航:苹果新纪... 苹果首席执行官蒂姆·库克将卸任,硬件工程主管约翰·特纳斯将接任,苹果公司今天宣布此事。 库克将在夏季...