> 文档中心 > Spring循环依赖原理&三级缓存源码详解(原创)

Spring循环依赖原理&三级缓存源码详解(原创)


❤️💛🧡💚💔🖤💜💙❤💕💞💓💗❣️💝💘💖❤️💛🧡💚💔🖤💜💙❤💕💞💓💗❣️💝💘💖💖💖 💖

❤️作者简介:2022新星计划第三季云原生与云计算赛道Top5🏅、华为云享专家🏅、云原生领域潜力新星🏅 💖

💛博客首页:C站个人主页🌞                                    💖

❤️💛🧡💚💔🖤💜💙❤💕💞💓💗❣️💝💘💖❤️💛🧡💚💔🖤💜💙❤💕💞💓💗❣️💝💘💖💖💖 💖

文章目录

    • Spring循环依赖原理&三级缓存源码详解
      • 几种循环依赖及其区别
        • 第一种:单例setter注入循环依赖(spring可解决)
        • 第二种:构造器注入循环依赖(无法解决)
        • 第三种:多例/原型setter注入循环依赖(无法解决)
      • 三级缓存源码详解
        • DefaultSingletonBeanRegistry
          • DefaultSingletonBeanRegistry定义的核心变量
          • registerSingleton注册单例bean底层源码
          • addSingleton添加单例bean底层源码
          • addSingletonFactory添加单例工厂源码
            • 为什么这段代码仅仅是移除二级缓存数据?
          • getSingleton(String beanName, boolean allowEarlyReference)底层源码
        • 深入Spring源码解析三级缓存(单例工厂)什么时候提前添加bean对象
          • AbstractAutowireCapableBeanFactory类
            • doCreateBean部分核心方法

Spring循环依赖原理&三级缓存源码详解

几种循环依赖及其区别

第一种:单例setter注入循环依赖(spring可解决)

  • 这种情况spring可以通过三级缓存去解决
@Componentpublic class AService {    @Autowired    private BService bService;    public BService getbService() { return bService;    }    public void setbService(BService bService) { this.bService = bService;    }    @Override    public String toString() { return "AService{" +  "bService=" + bService +  '}';    }}
@Componentpublic class BService {    @Autowired    private AService aService;    public AService getaService() { return aService;    }    public void setaService(AService aService) { this.aService = aService;    }}
  • 测试:
AService aService = (AService) applicationContext.getBean("AService");System.out.println(aService);
AService{bService=com.boot.csdn.bean.BService@2eed37f4}

第二种:构造器注入循环依赖(无法解决)

@Componentpublic class AService {    private BService bService;    AService(BService bService){ this.bService=bService;    }    @Override    public String toString() { return "AService{" +  "bService=" + bService +  '}';    }}
@Componentpublic class BService {    private AService aService;    BService(AService aService){ this.aService=aService;    }}
AService aService = (AService) applicationContext.getBean("AService");System.out.println(aService);
  • 测试:
***************************APPLICATION FAILED TO START***************************Description:The dependencies of some of the beans in the application context form a cycle:┌─────┐|  AService defined in file [D:\vue\CsdnVisitor\target\classes\com\boot\csdn\bean\AService.class]↑     ↓|  BService defined in file [D:\vue\CsdnVisitor\target\classes\com\boot\csdn\bean\BService.class]└─────┘Process finished with exit code 1

第三种:多例/原型setter注入循环依赖(无法解决)

@Component@Scope("prototype") //多例beanpublic class AService {    @Autowired    private BService bService;    public void setbService(BService bService) { this.bService = bService;    }    public BService getbService() { return bService;    }    @Override    public String toString() { return "AService{" +  "bService=" + bService +  '}';    }}
@Component@Scope("prototype")//多例beanpublic class BService {    @Autowired    private AService aService;    public void setaService(AService aService) { this.aService = aService;    }    public AService getaService() { return aService;    }}
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'AService': Requested bean is currently in creation: Is there an unresolvable circular reference?

三级缓存源码详解

DefaultSingletonBeanRegistry

DefaultSingletonBeanRegistry定义的核心变量
/** 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 ConcurrentHashMap<>(16);/** Cache of singleton factories: bean name to ObjectFactory. */private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);/** Set of registered singletons, containing the bean names in registration order. */private final Set<String> registeredSingletons = new LinkedHashSet<>(256);/** Names of beans that are currently in creation. */private final Set<String> singletonsCurrentlyInCreation =Collections.newSetFromMap(new ConcurrentHashMap<>(16));/** Collection of suppressed Exceptions, available for associating related causes. */@Nullableprivate Set<Exception> suppressedExceptions;
  • singletonObjects:单例池(一级缓存),key=beanName,value=bean对象
    • 存放的bean对象都是经过了完整的bean的生命周期。(比如依赖注入)
  • earlySingletonObjects:提前曝光的单例bean池(二级缓存),key=beanName,value=bean对象
    • 存放的bean对象没有经过完整的bean的生命周期,也就是没有进行依赖注入、初始化前、初始化、初始化等阶段。
  • singletonFactories:用来打破循环依赖最重要的一个缓存,单例bean工厂(三级缓存),key=beanName,value=对象工厂
registerSingleton注册单例bean底层源码
@Overridepublic void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {Assert.notNull(beanName, "Bean name must not be null");Assert.notNull(singletonObject, "Singleton object must not be null");synchronized (this.singletonObjects) {Object oldObject = this.singletonObjects.get(beanName);if (oldObject != null) {throw new IllegalStateException("Could not register object [" + singletonObject +"] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");}addSingleton(beanName, singletonObject);}}
  • 首先判断beanName和singletonObject是否为空,如果为空就抛出IllegalArgumentException异常;
  • 然后用synchronized锁住当前的singletonObjects对象,通过beanName去找到单例池对应的对象(oldObject),如果不为空(说明该bean已经存在单例池中)则报错。
  • 如果为空,说明该bean对象还没有存在于单例池中,说明可以把该对象添加进单例池,此时就会调用下面的addSingleton(beanName, singletonObject)方法去添加。
protected void addSingleton(String beanName, Object singletonObject) {synchronized (this.singletonObjects) {this.singletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}}
  • 同样,我们是把对象添加进单例池,所以为了线程安全我们要先锁住singletonObjects对象;
  • 调用this.singletonObjects.put(beanName, singletonObject)方法把对象添加进单例池;
  • 为了保持bean的单例性,会调用this.earlySingletonObjects.remove(beanName)和this.singletonFactories.remove(beanName)把二级缓存和三级缓存的该bean对象删除(即使里面没有,但是为了保持bean的单例性就要这样做)
  • 最后调用this.registeredSingletons.add(beanName)说明这个bean已经被注册。
addSingleton添加单例bean底层源码
protected void addSingleton(String beanName, Object singletonObject) {synchronized (this.singletonObjects) {this.singletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}}
  • 首先会把单例池锁住(防止线程不安全,虽然说其实现是ConcurrentHashMap);
  • 把beanName和singletonObject放入单例池;
  • 与此同时会通过beanName移除二级缓存以及三级缓存对应的数据(保持单例性)
  • 最后把beanName放入registeredSingletons集合,说明该bean已经注册成功。
addSingletonFactory添加单例工厂源码
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(singletonFactory, "Singleton factory must not be null");synchronized (this.singletonObjects) {if (!this.singletonObjects.containsKey(beanName)) {this.singletonFactories.put(beanName, singletonFactory);this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}}}
  • 首先判断单例工厂不能为空,其次就锁住单例池;
  • 如果单例池没有这个bean对象,则会往三级缓存放单例工厂;
  • 同时会通过这个beanName移除对应的二级缓存数据;
  • 且标记为已注册。
为什么这段代码仅仅是移除二级缓存数据?

因为我们是在判断单例池(一级缓存)没有该数据的时候才会往三级缓存放单例工厂,此时又因为我们在一开始就把单例池给锁住,此时是线程安全的,且一级缓存本来就没有该数据,所以不需要多此一举。

getSingleton(String beanName, boolean allowEarlyReference)底层源码
/** * Return the (raw) singleton object registered under the given name. * 

Checks already instantiated singletons and also allows for an early * reference to a currently created singleton (resolving a circular reference). * @param beanName the name of the bean to look for * @param allowEarlyReference whether early references should be created or not * @return the registered singleton object, or {@code null} if none found */@Nullableprotected Object getSingleton(String beanName, boolean allowEarlyReference) {// Quick check for existing instance without full singleton lockObject singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {synchronized (this.singletonObjects) {// Consistent creation of early reference within full singleton locksingletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null) {ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}}}return singletonObject;}

public boolean isSingletonCurrentlyInCreation(String beanName) {   return this.singletonsCurrentlyInCreation.contains(beanName);}
  • 首先会通过beanName去单例池(一级缓存)里面取singletonObject;
  • 如果取不到(singletonObject==null),并且这个bean正在创建中(isSingletonCurrentlyInCreation(beanName)==true),此时就会去二级缓存里面找
  • 如果二级缓存找不到,并且允许提前引用(allowEarlyReference==true),此时就会锁住单例池;
  • 然后再此从单例池中找,找不到的话就会去二级缓存找;
  • 二级缓存找不到的话,就会通过beanName拿到单例工厂,如果该单例工厂存在(一般都会存在);
  • 就会通过singletonFactory.getObject()去拿到半成品的bean(也就是没有进行完整bean的生命周期的bean),并且放到二级缓存中,同时三级缓存将会删除掉这个单例工厂。

深入Spring源码解析三级缓存(单例工厂)什么时候提前添加bean对象

AbstractAutowireCapableBeanFactory类
doCreateBean部分核心方法
// Eagerly cache singletons to be able to resolve circular references// even when triggered by lifecycle interfaces like BeanFactoryAware.boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {if (logger.isTraceEnabled()) {logger.trace("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");}addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}
  • 这段代码就展示了bean对象在什么条件下会添加到三级缓存来解决循环依赖:
  • 首先会进行如下三个条件判断(并且只要有一个不符合,该bean就不会提前放到三级缓存中):
  • 1:该Bean必须是单例;
  • 2:必须允许循环依赖(只有允许了循环依赖,Spring才会出手解决循环依赖问题);
  • 3:这个bean必须正在被创建(就是说这个bean还没创建完,此时可能出现了循环依赖现象)

符合了上面三个条件(必须单例、必须允许循环依赖、该bean必须正在被创建),就会提前把不完整对象封装成一个单例工厂放到三级缓存中。