This is the 14th day of my participation in the August More Text Challenge. For details, see: August More Text Challenge

Spring cyclic dependencies and level 3 caching

Circular dependencies occur when two classes A and B depend on each other, and when multiple classes depend on each other.

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

  public String say(a){
    return "hello"; }}@Service
public class ServiceB {

  @Autowired
  private ServiceA serviceA;

}
Copy the code

When the constructor injects a dependency, the loop has no solution

The dependencies of some of the beans in the application context form a cycle: cycleController (field com.paw.cycle.dependency.service.ServiceA Com. Paw. Cycle. The dependency. Controller. CycleController. ServiceA) ┌ ─ ─ ─ ─ ─ ┐ | serviceA defined in the file [/ Users /... / target/classes/com/paw/cycle/dependency/service/ServiceA class] write left | serviceB defined in the file [/ Users /... / target/classes/com/paw/cycle/dependency/service/ServiceB class] └ ─ ─ ─ ─ ─ ┘Copy the code

How does Spring solve circular dependencies

Open the log

logging:
  level:
    org.springframework: trace
Copy the code

Run view Logs

main] o.s.b.f.s.DefaultListableBeanFactory : Finished creating instance of bean 'cycleDependencyApplication' main] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory' main] o.s.b.f.s.DefaultListableBeanFactory  : Creating shared instance of singleton bean 'cycleController' main] o.s.b.f.s.DefaultListableBeanFactory : Creating instance of bean 'cycleController' main] o.s.b.f.s.DefaultListableBeanFactory : Eagerly caching bean 'cycleController' to allow for resolving potential circular references main] o.s.b.f.s.DefaultListableBeanFactory : Finished creating instance of bean 'cycleController' main] o.s.b.f.s.DefaultListableBeanFactory : Creating shared instance of singleton bean 'serviceA' main] o.s.b.f.s.DefaultListableBeanFactory : Creating instance of bean 'serviceA' main] o.s.b.f.annotation.InjectionMetadata : Registered injected element on class [com.paw.cycle.dependency.service.ServiceA]: AutowiredFieldElement for private com.paw.cycle.dependency.service.ServiceB com.paw.cycle.dependency.service.ServiceA.serviceB main] o.s.b.f.s.DefaultListableBeanFactory : Eagerly caching bean 'serviceA' to allow for resolving potential circular references main] o.s.b.f.annotation.InjectionMetadata : Processing injected element of bean 'serviceA': AutowiredFieldElement for private com.paw.cycle.dependency.service.ServiceB com.paw.cycle.dependency.service.ServiceA.serviceB main] o.s.b.f.s.DefaultListableBeanFactory : Creating shared instance of singleton bean 'serviceB' main] o.s.b.f.s.DefaultListableBeanFactory : Creating instance of bean 'serviceB' main] o.s.b.f.annotation.InjectionMetadata : Registered injected element on class [com.paw.cycle.dependency.service.ServiceB]: AutowiredFieldElement for private com.paw.cycle.dependency.service.ServiceA com.paw.cycle.dependency.service.ServiceB.serviceA main] o.s.b.f.s.DefaultListableBeanFactory : Eagerly caching bean 'serviceB' to allow for resolving potential circular references main] o.s.b.f.annotation.InjectionMetadata : Processing injected element of bean 'serviceB': AutowiredFieldElement for private com.paw.cycle.dependency.service.ServiceA com.paw.cycle.dependency.service.ServiceB.serviceA main] o.s.b.f.s.DefaultListableBeanFactory : Returning eagerly cached instance of singleton bean 'serviceA' that is not fully initialized yet - a consequence of a circular reference main] f.a.AutowiredAnnotationBeanPostProcessor : Autowiring by type from bean name 'serviceB' to bean named 'serviceA' main] o.s.b.f.s.DefaultListableBeanFactory : Finished creating instance of bean 'serviceB' main] f.a.AutowiredAnnotationBeanPostProcessor : Autowiring by type from bean name 'serviceA' to bean named 'serviceB' main] o.s.b.f.s.DefaultListableBeanFactory : Finished creating instance of bean 'serviceA' main] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of sinleton bean 'serviceB'Copy the code

The basic process can be sorted out:

Create ing serviceA==> Inject serviceB==> Inject a cache semi-finished product ServiceA ==> Creating serviceB==> Injecting serviceB==> Creating serviceA.

DefaultSingletonBeanRegistry l3 cache

// Level 1 instantiated bean
/**Cache of singleton objects: bean name to bean instance.*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

// The second-level semi-finished product has no attribute injection
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

// Generate the ObjectFactory for the bean
/** Cache of singleton factories: bean name to ObjectFactory. */
private finalMap<String, ObjectFactory<? >> singletonFactories =new HashMap<>(16);

Copy the code

Gets the bean instance object

First from the first level, then from the second level, and finally from the third level

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // Level 1 cache
 Object singletonObject = this.singletonObjects.get(beanName);
 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
  synchronized (this.singletonObjects) {
      // Level 2 cache
   singletonObject = this.earlySingletonObjects.get(beanName);
   if (singletonObject == null&& allowEarlyReference) { ObjectFactory<? > singletonFactory =this.singletonFactories.get(beanName);
    if(singletonFactory ! =null) {
        // Level 3 cache
     singletonObject = singletonFactory.getObject();
     this.earlySingletonObjects.put(beanName, singletonObject);
     this.singletonFactories.remove(beanName); }}}}return singletonObject;
}
Copy the code

Add a conditional break point, and start moving

beanName.equals(“serviceA”)

Class

SimpleAliasRegistry (org.springframework.core)

|-DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)

|-|-FactoryBeanRegistrySupport (org.springframework.beans.factory.support)

|-|-|-AbstractBeanFactory (org.springframework.beans.factory.support)

|-|-|-|-AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)

|-|-|-|-|-DefaultListableBeanFactory (org.springframework.beans.factory.support)

|-|-|-|-|-XmlBeanFactory (org.springframework.beans.factory.xml)

L3 cache objectFactory by an anonymous inner class (anonymous class calls the AbstractAutowireCapableBeanFactory# getEarlyBeanReference method) getEarlyBeanReference Get early semi-finished objects.

AbstractAutowireCapableBeanFactory
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
  throws BeanCreationException {... addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); . }Copy the code

singletonFactory AbstractAutowireCapableBeanFactory

// Add level 3 cache
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); }}}Copy the code

// Create the bean with the ObjectFactory

AbstractBeanFactory#doGetBean
// Create the bean instance
sharedInstance = getSingleton(beanName, () -> {
 try {
  returncreateBean(beanName, mbd, args); }... } ==> DefaultSingletonBeanRegistry#getSingleton
public Object getSingleton(String beanName, ObjectFactory
        singletonFactory) {
 Assert.notNull(beanName, "Bean name must not be null");
 synchronized (this.singletonObjects) {
  Object singletonObject = this.singletonObjects.get(beanName);
  if (singletonObject == null) {
   if (this.singletonsCurrentlyInDestruction) {
    throw new BeanCreationNotAllowedException(beanName,
      "Singleton bean creation not allowed while singletons of this factory are in destruction " +
      "(Do not request a bean from a BeanFactory in a destroy method implementation!) ");
   }
   if (logger.isDebugEnabled()) {
    logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
   }
   // Processing before creating the bean
   beforeSingletonCreation(beanName);
   boolean newSingleton = false;
   boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
   if (recordSuppressedExceptions) {
    this.suppressedExceptions = new LinkedHashSet<>();
   }
   try {
       // Get the bean object instance from the level-three cached objectFactory
    singletonObject = singletonFactory.getObject();
    newSingleton = true;
   }
   catch (IllegalStateException ex) {
    // Has the singleton object implicitly appeared in the meantime ->
    // if yes, proceed with it since the exception indicates that state.
    singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null) {
     throwex; }}catch (BeanCreationException ex) {
    if (recordSuppressedExceptions) {
     for (Exception suppressedException : this.suppressedExceptions) { ex.addRelatedCause(suppressedException); }}throw ex;
   }
   finally {
    if (recordSuppressedExceptions) {
     this.suppressedExceptions = null;
    }
    // Processing after creation
    afterSingletonCreation(beanName);
   }
   if (newSingleton) {
       // Added to level 1 cache successfullyaddSingleton(beanName, singletonObject); }}return singletonObject;
 }
Copy the code

AbstractAutowireCapableBeanFactory create bean

Add it to the Level-iii cache ==> Create a caching bean. Inject bean properties and instantiate the bean

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
  throws BeanCreationException {

 // Instantiate the bean.
 BeanWrapper instanceWrapper = null;
 if (mbd.isSingleton()) {
  instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
 }
 if (instanceWrapper == null) {
  instanceWrapper = createBeanInstance(beanName, mbd, args);
 }
 finalObject bean = instanceWrapper.getWrappedInstance(); Class<? > beanType = instanceWrapper.getWrappedClass();if(beanType ! = NullBean.class) { mbd.resolvedTargetType = beanType; }// Allow post-processors to modify the merged bean definition.
 synchronized (mbd.postProcessingLock) {
  if(! mbd.postProcessed) {try {
    applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
   }
   catch (Throwable ex) {
    throw new BeanCreationException(mbd.getResourceDescription(), beanName,
      "Post-processing of merged bean definition failed", ex);
   }
   mbd.postProcessed = true; }}// 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");
  }
  // Add the level 3 cache beanFactory
  addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
 }

 // Initialize the bean instance.
 Object exposedObject = bean;
 try {
 // Fill the bean property to inject @autoWired field
  populateBean(beanName, mbd, instanceWrapper);
  exposedObject = initializeBean(beanName, exposedObject, mbd);
 }
 catch (Throwable ex) {
  if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
   throw (BeanCreationException) ex;
  }
  else {
   throw new BeanCreationException(
     mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); }}if (earlySingletonExposure) {
  Object earlySingletonReference = getSingleton(beanName, false);
  if(earlySingletonReference ! =null) {
   if (exposedObject == bean) {
    exposedObject = earlySingletonReference;
   }
   else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
    String[] dependentBeans = getDependentBeans(beanName);
    Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
    for (String dependentBean : dependentBeans) {
     if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
      actualDependentBeans.add(dependentBean);
     }
    }
    if(! actualDependentBeans.isEmpty()) {throw new BeanCurrentlyInCreationException(beanName,
       "Bean with name '" + beanName + "' has been injected into other beans [" +
       StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
       "] in its raw version as part of a circular reference, but has eventually been " +
       "wrapped. This means that said other beans do not use the final version of the " +
       "bean. This is often the result of over-eager type matching - consider using " +
       "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); }}}}// Register bean as disposable.
 try {
  registerDisposableBeanIfNecessary(beanName, bean, mbd);
 }
 catch (BeanDefinitionValidationException ex) {
  throw new BeanCreationException(
    mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
 }

 return exposedObject;
}
Copy the code

Property injection serviceB

Fill attribute

//AbstractAutowireCapableBeanFactory
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) 
Copy the code

AbstractAutowireCapableBeanFactory#postProcessProperties

InjectionMetadata class for attribute injection

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
 InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
 try {
  metadata.inject(bean, beanName, pvs);
 }
 catch (BeanCreationException ex) {
  throw ex;
 }
 catch (Throwable ex) {
  throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
 }
 return pvs;
}

Copy the code

The condition of the AOP singletonObject = singletonFactory. GetObject (); Multiple calls generate new proxy objects (the same Bean object serviceA is represented by different proxy objects). Spring puts the singletonObject proxy object into the second-level cache, which is also injected into the serviceB proxy object singletonObject.

It’s true that without AOP, two-level caching would solve the problem of circular dependencies. With AOP, two-level caching would not solve the problem. It’s impossible to execute singleFactory.getobject () every time I generate a new proxy object, so I have to use another cache to hold the generated proxy object.

Conclusion:

Three-level cache to store in the spring the ObjectFactory (anonymous class calls the AbstractAutowireCapableBeanFactory # getEarlyBeanReference method), semi-finished products obtained through getObect object in the second level cache, After completing the property injection and bean initialization, it is put into the level 1 cache. In the case of an AOP proxy, the third-level cache generates a new proxy object at a time, so put the getObject generated proxy object singletonObject into the second-level cache and add the proxy object to the first-level cache after property injection.