java面试题Spring部分(四)

avatar
作者
猴君
阅读量:0

一、什么事spring的三级缓存

典型回答

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

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

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!