Skip to content

面经资料

spring 三级缓存是什么?

三级缓存主要是为了解决单例模式下的循环依赖的问题。

循环依赖指的是两个类中的属性相互依赖对方:例如 A 类中有 B 属性,B 类中有 A属性,从而形成了一个依赖闭环,如下图。

image

循环依赖问题在Spring中主要有三种情况:

  • 第一种:通过构造方法进行依赖注入时产生的循环依赖问题。
  • 第二种:通过setter方法进行依赖注入且是在多例(原型)模式下产生的循环依赖问题。
  • 第三种:通过setter方法进行依赖注入且是在单例模式下产生的循环依赖问题。

第一种示例,构造方法循环依赖(无法解决)

java
@Service  
public class ServiceA {  
    private final ServiceB serviceB;  
    @Autowired  
    public ServiceA(ServiceB serviceB) {  
        this.serviceB = serviceB;  
    }  
}  

@Service  
public class ServiceB {  
    private final ServiceA serviceA;  
    @Autowired  
    public ServiceB(ServiceA serviceA) {  
        this.serviceA = serviceA;  
    }  
}

第二种示例,多例模式的 ServiceC 和 ServiceD

java
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)  
@Service  
public class ServiceC {  
    private ServiceD serviceD;  
    @Autowired  
    public void setServiceD(ServiceD serviceD) {  
        this.serviceD = serviceD;  
    }  
}  

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)  
@Service  
public class ServiceD {  
    private ServiceC serviceC;  
    @Autowired  
    public void setServiceC(ServiceC serviceC) {  
        this.serviceC = serviceC;  
    }  
}

第三种:Setter+单例模式循环依赖(可以解决)

java
@Service  
public class ServiceE {  
    private ServiceF serviceF;  
    @Autowired  
    public void setServiceF(ServiceF serviceF) {  
        this.serviceF = serviceF;  
    }  
}  

@Service  
public class ServiceF {  
    private ServiceE serviceE;  
    @Autowired  
    public void setServiceE(ServiceE serviceE) {  
        this.serviceE = serviceE;  
    }  
}

Spring的三重缓存机制

  • singletonObjects 完全初始化好的单例Bean 直接获取可用Bean

  • earlySingletonObjects 提前暴露的未完成初始化的Bean 解决循环依赖

  • singletonFactories 生成早期引用的ObjectFactory 延迟处理Bean的后处理器

  • 面试话术 "Spring解决循环依赖的核心在于三级缓存机制,但这仅适用于单例模式下的Setter/字段注入。构造器注入因必须在创建阶段完成依赖注入,而原型模式因无法缓存中间状态,均会导致无法解决的循环问题。理解这一点对设计松耦合的Spring应用至关重要。"

三级缓存都是ConcurrentHashMap吗?

不是,只有一级缓存是ConcurrentHashMap,三级缓存也就是DefaultSingletonBeanRegistry中的三个Map:

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

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

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

但是在 spring 2.7.10新版本开始,earlySingletonObjects(二级缓存)改成使用 ConcurrentHashMap。

继承和接口的区别?什么时候用继承,什么时候用接口呢?

继承接口
子类拥有父类的所有属性和方法子类必须实现接口中的所有方法
是"is-a"的关系是"can-do"的关系
提供代码复用要实现所有接口方法

如何选择? 优先用接口:

需要定义行为规范而非具体实现(如Comparable定义比较逻辑)。

类需要多种不相关的能力(如既能保存到数据库,又能导出为JSON)。

优先用继承:

存在清晰的层次关系且需要代码复用(如Exception类继承体系)。

需要覆盖父类方法实现多态(如Animal的makeSound()方法被不同子类重写)。

wow!