SpringBoot three-level cache solves circular dependency source code analysis

1. Possible problems if L3 cache is not used

In the SpringBoot framework, if there is only a two-level cache, there may be an abnormal object creation process when a circular dependency occurs, as shown in the figure below:

  1. Create a blank object a1 of A
  2. After parsing and filling the attributes of the A object and finding that the dependent B object has not been created, the creation of the B object is triggered.
  3. In the process of creating the B object, when filling in the object properties, it is found that it depends on the A object. At this time, a1 is obtained from the cache, and the B object is created.
  4. The attributes of A object are filled in, the enhanced aspect is applied to process object a1, and the proxy object a2 is created.

As you can see, the core of the problem is that in the process of processing after object A属性填充 is completed, two different objects may be created. A object, violating the default singleton principle. To solve this problem, the solution is to use the third-level cache to provide a trigger point for creating the proxy object in advance, and use the cache to mark that the proxy of the target object has been created代理对象后置创建

Insert image description here

2. Source code analysis

Insert image description here

2.1 Creation process of object instances

  1. The object factory instance in the SpringBoot framework defaults to DefaultListableBeanFactory. Obtaining objects from the factory will in most cases call its parent class implementation AbstractBeanFactory#getBean(). The core logic of this method is actually to call the AbstractBeanFactory#doGetBean() method. The points to note with this method are as follows:

    1. First call the parent class DefaultSingletonBeanRegistry#getSingleton() method to try to obtain the target object from the cache. Once obtained, there is no need to go through the creation logic. We will not go into details in this section
    2. If the target object cannot be obtained from the cache, the parent class overloaded method is called DefaultSingletonBeanRegistry#getSingleton() to create the object logic. When calling this method, the Lambda expression will be passed in as the functional interface implementation of ObjectFactory, which is used to trigger object creation
    3. Call AbstractBeanFactory#getObjectForBeanInstance() After processing FactoryBean, return the object
     public Object getBean(String name) throws BeansException {
          
          
         return doGetBean(name, null, null, false);
     }
     
     protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
             @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
          
          
    
         final String beanName = transformedBeanName(name);
         Object bean;
    
         // Eagerly check singleton cache for manually registered singletons.
         Object sharedInstance = getSingleton(beanName);
         if (sharedInstance != null && args == null) {
          
          
             if (logger.isTraceEnabled()) {
          
          
                 if (isSingletonCurrentlyInCreation(beanName)) {
          
          
                     logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
                             "' that is not fully initialized yet - a consequence of a circular reference");
                 }
                 else {
          
          
                     logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
                 }
             }
             bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
         }
    
         else {
          
          
             // Fail if we're already creating this bean instance:
             // We're assumably within a circular reference.
             if (isPrototypeCurrentlyInCreation(beanName)) {
          
          
                 throw new BeanCurrentlyInCreationException(beanName);
             }
    
             // Check if bean definition exists in this factory.
             BeanFactory parentBeanFactory = getParentBeanFactory();
             if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
          
          
                 // Not found -> check parent.
                 String nameToLookup = originalBeanName(name);
                 if (parentBeanFactory instanceof AbstractBeanFactory) {
          
          
                     return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                             nameToLookup, requiredType, args, typeCheckOnly);
                 }
                 else if (args != null) {
          
          
                     // Delegation to parent with explicit args.
                     return (T) parentBeanFactory.getBean(nameToLookup, args);
                 }
                 else if (requiredType != null) {
          
          
                     // No args -> delegate to standard getBean method.
                     return parentBeanFactory.getBean(nameToLookup, requiredType);
                 }
                 else {
          
          
                     return (T) parentBeanFactory.getBean(nameToLookup);
                 }
             }
    
             if (!typeCheckOnly) {
          
          
                 markBeanAsCreated(beanName);
             }
    
             try {
          
          
                 final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                 checkMergedBeanDefinition(mbd, beanName, args);
    
                 // Guarantee initialization of beans that the current bean depends on.
                 String[] dependsOn = mbd.getDependsOn();
                 if (dependsOn != null) {
          
          
                     for (String dep : dependsOn) {
          
          
                         if (isDependent(beanName, dep)) {
          
          
                             throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                     "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                         }
                         registerDependentBean(dep, beanName);
                         try {
          
          
                             getBean(dep);
                         }
                         catch (NoSuchBeanDefinitionException ex) {
          
          
                             throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                     "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                         }
                     }
                 }
    
                 // Create bean instance.
                 if (mbd.isSingleton()) {
          
          
                     sharedInstance = getSingleton(beanName, () -> {
          
          
                         try {
          
          
                             return createBean(beanName, mbd, args);
                         }
                         catch (BeansException ex) {
          
          
                             // Explicitly remove instance from singleton cache: It might have been put there
                             // eagerly by the creation process, to allow for circular reference resolution.
                             // Also remove any beans that received a temporary reference to the bean.
                             destroySingleton(beanName);
                             throw ex;
                         }
                     });
                     bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                 }
    
                 else if (mbd.isPrototype()) {
          
          
                     // It's a prototype -> create a new instance.
                     Object prototypeInstance = null;
                     try {
          
          
                         beforePrototypeCreation(beanName);
                         prototypeInstance = createBean(beanName, mbd, args);
                     }
                     finally {
          
          
                         afterPrototypeCreation(beanName);
                     }
                     bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                 }
    
                 else {
          
          
                     String scopeName = mbd.getScope();
                     final Scope scope = this.scopes.get(scopeName);
                     if (scope == null) {
          
          
                         throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                     }
                     try {
          
          
                         Object scopedInstance = scope.get(beanName, () -> {
          
          
                             beforePrototypeCreation(beanName);
                             try {
          
          
                                 return createBean(beanName, mbd, args);
                             }
                             finally {
          
          
                                 afterPrototypeCreation(beanName);
                             }
                         });
                         bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                     }
                     catch (IllegalStateException ex) {
          
          
                         throw new BeanCreationException(beanName,
                                 "Scope '" + scopeName + "' is not active for the current thread; consider " +
                                 "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                                 ex);
                     }
                 }
             }
             catch (BeansException ex) {
          
          
                 cleanupAfterBeanCreationFailure(beanName);
                 throw ex;
             }
         }
    
         // Check if required type matches the type of the actual bean instance.
         if (requiredType != null && !requiredType.isInstance(bean)) {
          
          
             try {
          
          
                 T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
                 if (convertedBean == null) {
          
          
                     throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
                 }
                 return convertedBean;
             }
             catch (TypeMismatchException ex) {
          
          
                 if (logger.isTraceEnabled()) {
          
          
                     logger.trace("Failed to convert bean '" + name + "' to required type '" +
                             ClassUtils.getQualifiedName(requiredType) + "'", ex);
                 }
                 throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
             }
         }
         return (T) bean;
     }
    
  2. DefaultSingletonBeanRegistry#getSingleton()The core logic of the method is concise, and the key points are as follows:

    1. First, lock the first-level cache to prevent concurrency problems, and then check that there is no target object in the first-level cache before starting to create it.
    2. The steps to create an object are not complicated. First call the DefaultSingletonBeanRegistry#beforeSingletonCreation() method to mark that the target object is being created, and then call the passed functional implementationObjectFactory#getObject() to start. Create an object. After the object is created, call the DefaultSingletonBeanRegistry#afterSingletonCreation() method to remove the creation mark of the target object
    3. Finally call the DefaultSingletonBeanRegistry#addSingleton() method to move the created new object into the first-level cache and remove it from other caches
     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 + "'");
                 }
                 beforeSingletonCreation(beanName);
                 boolean newSingleton = false;
                 boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
                 if (recordSuppressedExceptions) {
          
          
                     this.suppressedExceptions = new LinkedHashSet<>();
                 }
                 try {
          
          
                     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) {
          
          
                         throw ex;
                     }
                 }
                 catch (BeanCreationException ex) {
          
          
                     if (recordSuppressedExceptions) {
          
          
                         for (Exception suppressedException : this.suppressedExceptions) {
          
          
                             ex.addRelatedCause(suppressedException);
                         }
                     }
                     throw ex;
                 }
                 finally {
          
          
                     if (recordSuppressedExceptions) {
          
          
                         this.suppressedExceptions = null;
                     }
                     afterSingletonCreation(beanName);
                 }
                 if (newSingleton) {
          
          
                     addSingleton(beanName, singletonObject);
                 }
             }
             return singletonObject;
         }
     }
    
  3. ObjectFactory#getObject()The implementation of has been mentioned in step 1 of this section. In fact, the AbstractAutowireCapableBeanFactory#createBean() method will eventually be called. The core logic of this method is to call AbstractAutowireCapableBeanFactory#doCreateBean() Method

     protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
             throws BeanCreationException {
          
          
    
         if (logger.isTraceEnabled()) {
          
          
             logger.trace("Creating instance of bean '" + beanName + "'");
         }
         RootBeanDefinition mbdToUse = mbd;
    
         // Make sure bean class is actually resolved at this point, and
         // clone the bean definition in case of a dynamically resolved Class
         // which cannot be stored in the shared merged bean definition.
         Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
         if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
          
          
             mbdToUse = new RootBeanDefinition(mbd);
             mbdToUse.setBeanClass(resolvedClass);
         }
    
         // Prepare method overrides.
         try {
          
          
             mbdToUse.prepareMethodOverrides();
         }
         catch (BeanDefinitionValidationException ex) {
          
          
             throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
                     beanName, "Validation of method overrides failed", ex);
         }
    
         try {
          
          
             // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
             Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
             if (bean != null) {
          
          
                 return bean;
             }
         }
         catch (Throwable ex) {
          
          
             throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
                     "BeanPostProcessor before instantiation of bean failed", ex);
         }
    
         try {
          
          
             Object beanInstance = doCreateBean(beanName, mbdToUse, args);
             if (logger.isTraceEnabled()) {
          
          
                 logger.trace("Finished creating instance of bean '" + beanName + "'");
             }
             return beanInstance;
         }
         catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
          
          
             // A previously detected exception with proper bean creation context already,
             // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
             throw ex;
         }
         catch (Throwable ex) {
          
          
             throw new BeanCreationException(
                     mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
         }
     }
    
  4. AbstractAutowireCapableBeanFactory#doCreateBean()The method is not complicated, and the core points are as follows:

    1. First call the AbstractAutowireCapableBeanFactory#createBeanInstance() method to create a blank object of the target class using reflection
    2. Then call the DefaultSingletonBeanRegistry#addSingletonFactory() method to obtain the object factory of the current object and store it in the third-level cache
    3. Then call the AbstractAutowireCapableBeanFactory#populateBean() method to fill in the attributes of the blank object
    4. Continue to call the AbstractAutowireCapableBeanFactory#initializeBean() method to enhance the target object, including using aspects to create proxy objects, etc.
    5. Finally call theDefaultSingletonBeanRegistry#getSingleton() method to obtain the object from the third-level cache, and then verify whether the object after a series of processing is consistent with the original object. If they are consistent, use the object in the cache to replace it. If the original object is removed, inconsistency means that there are multiple target instances in the container and an exception needs to be thrown
     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);
         }
         final Object 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");
             }
             addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
         }
    
         // Initialize the bean instance.
         Object exposedObject = bean;
         try {
          
          
             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;
     }
    
  5. AbstractAutowireCapableBeanFactory#populateBean()The method will handle the attribute dependencies within the object. The core processing is divided into the following steps:

    1. First call RootBeanDefinition#getPropertyValues() to obtain all properties of the target object directly from the Bean definition encapsulated object
    2. Then get all post-processors from the container and use the specific post-processor InstantiationAwareBeanPostProcessor#postProcessProperties() method to process the dependent properties in the object
     protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
          
          
         if (bw == null) {
          
          
             if (mbd.hasPropertyValues()) {
          
          
                 throw new BeanCreationException(
                         mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
             }
             else {
          
          
                 // Skip property population phase for null instance.
                 return;
             }
         }
    
         // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
         // state of the bean before properties are set. This can be used, for example,
         // to support styles of field injection.
         if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
          
          
             for (BeanPostProcessor bp : getBeanPostProcessors()) {
          
          
                 if (bp instanceof InstantiationAwareBeanPostProcessor) {
          
          
                     InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                     if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
          
          
                         return;
                     }
                 }
             }
         }
    
         PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
    
         int resolvedAutowireMode = mbd.getResolvedAutowireMode();
         if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
          
          
             MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
             // Add property values based on autowire by name if applicable.
             if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
          
          
                 autowireByName(beanName, mbd, bw, newPvs);
             }
             // Add property values based on autowire by type if applicable.
             if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
          
          
                 autowireByType(beanName, mbd, bw, newPvs);
             }
             pvs = newPvs;
         }
    
         boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
         boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
    
         PropertyDescriptor[] filteredPds = null;
         if (hasInstAwareBpps) {
          
          
             if (pvs == null) {
          
          
                 pvs = mbd.getPropertyValues();
             }
             for (BeanPostProcessor bp : getBeanPostProcessors()) {
          
          
                 if (bp instanceof InstantiationAwareBeanPostProcessor) {
          
          
                     InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                     PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
                     if (pvsToUse == null) {
          
          
                         if (filteredPds == null) {
          
          
                             filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
                         }
                         pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                         if (pvsToUse == null) {
          
          
                             return;
                         }
                     }
                     pvs = pvsToUse;
                 }
             }
         }
         if (needsDepCheck) {
          
          
             if (filteredPds == null) {
          
          
                 filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
             }
             checkDependencies(beanName, mbd, filteredPds, pvs);
         }
    
         if (pvs != null) {
          
          
             applyPropertyValues(beanName, mbd, bw, pvs);
         }
     }
    
  6. AutowiredAnnotationBeanPostProcessor#postProcessProperties()The method will process the attributes marked by @Autowired/@Value/@Inject. The core processing is as follows:

    1. CallAutowiredAnnotationBeanPostProcessor#findAutowiringMetadata() method to find out the attribute fields and methods marked by @Autowired/@Value/@Inject in the class, and encapsulate them into corresponding structures, such as attribute fields CorrespondenceAutowiredFieldElement
    2. Call InjectionMetadata#inject() method to start dependency 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;
     }
    
  7. For field injection, the AutowiredAnnotationBeanPostProcessor#AutowiredFieldElement#inject() method will eventually be called to execute the injection logic. The key steps are as follows:

    1. First calls DefaultListableBeanFactory#resolveDependency() to process the dependency through the object factory, and finally calls the DefaultListableBeanFactory#getBean() method to obtain the dependent object. If there is currently a A->B->A circular dependency, the process of creating the B object is the repetition of steps 1-7 in this section, and the DefaultListableBeanFactory#getBean() method will eventually be called to obtain 创建中的 A 对象, continue the analysis in the next section of this part
    2. After obtaining the dependent object, inject it into the specified field through reflection
         protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
          
          
             Field field = (Field) this.member;
             Object value;
             if (this.cached) {
          
          
                 value = resolvedCachedArgument(beanName, this.cachedFieldValue);
             }
             else {
          
          
                 DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
                 desc.setContainingClass(bean.getClass());
                 Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
                 Assert.state(beanFactory != null, "No BeanFactory available");
                 TypeConverter typeConverter = beanFactory.getTypeConverter();
                 try {
          
          
                     value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
                 }
                 catch (BeansException ex) {
          
          
                     throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
                 }
                 synchronized (this) {
          
          
                     if (!this.cached) {
          
          
                         if (value != null || this.required) {
          
          
                             this.cachedFieldValue = desc;
                             registerDependentBeans(beanName, autowiredBeanNames);
                             if (autowiredBeanNames.size() == 1) {
          
          
                                 String autowiredBeanName = autowiredBeanNames.iterator().next();
                                 if (beanFactory.containsBean(autowiredBeanName) &&
                                         beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
          
          
                                     this.cachedFieldValue = new ShortcutDependencyDescriptor(
                                             desc, autowiredBeanName, field.getType());
                                 }
                             }
                         }
                         else {
          
          
                             this.cachedFieldValue = null;
                         }
                         this.cached = true;
                     }
                 }
             }
             if (value != null) {
          
          
                 ReflectionUtils.makeAccessible(field);
                 field.set(bean, value);
             }
         }
    

2.2 Processing of Level 3 Cache

  1. In step 1 of the previous section, the author mentioned that when obtaining an object from the container, it first obtains it from the cache. Then for the object A being created, the DefaultSingletonBeanRegistry#getSingleton() method will first be called. When obtaining an instance from the internal L3 cache, you need to pay attention to the following points:

    1. First obtain the created singleton object from the first-level cache singletonObjects
    2. If it cannot be obtained from the first-level cache, determine whether the object currently to be obtained is being created, and if so, obtain it from the second-level cache earlySingletonObjects
    3. If it cannot be obtained from the second-level cache, determine whether it is allowed to use the reference exposed in advance to obtain the object. If so, obtain the object factory from the third-level cachesingletonFactories and call The object factory method obtains the object. After obtaining the object, store it in the second-level cache and remove the object factory from the third-level cache
    public Object getSingleton(String beanName) {
          
          
         return getSingleton(beanName, true);
     }
    
     /**
      * Return the (raw) singleton object registered under the given name.
      * <p>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
      */
      protected Object getSingleton(String beanName, boolean allowEarlyReference) {
          
          
         Object singletonObject = this.singletonObjects.get(beanName);
         if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
          
          
             synchronized (this.singletonObjects) {
          
          
                 singletonObject = this.earlySingletonObjects.get(beanName);
                 if (singletonObject == null && allowEarlyReference) {
          
          
                     ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                     if (singletonFactory != null) {
          
          
                         singletonObject = singletonFactory.getObject();
                         this.earlySingletonObjects.put(beanName, singletonObject);
                         this.singletonFactories.remove(beanName);
                     }
                 }
             }
         }
         return singletonObject;
     }
    
  2. In step 4 of the previous section, the author mentioned that the third-level cache is actually stored in the object factory, and the object is obtained through the functional interface of the object factory, then this will be triggeredAbstractAutowireCapableBeanFactory#getEarlyBeanReference() Method execution. It can be seen that the core processing here is to traverse the SmartInstantiationAwareBeanPostProcessor post-processor to process the target object. During this process, the AnnotationAwareAspectJAutoProxyCreator#getEarlyBeanReference() method will be called and finally executed. Parent class implementationAbstractAutoProxyCreator#getEarlyBeanReference()

     protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
          
          
         Object exposedObject = bean;
         if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
          
          
             for (BeanPostProcessor bp : getBeanPostProcessors()) {
          
          
                 if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
          
          
                     SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                     exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                 }
             }
         }
         return exposedObject;
      }
    
  3. AbstractAutoProxyCreator#getEarlyBeanReference()The key processing of the method is divided into two steps:

    1. First generate the cache key of the object and store the original object in the internal earlyProxyReferences cache. Please note that this cache is to avoid repeatedly creating the proxy object of the target object
    2. Call AbstractAutoProxyCreator#wrapIfNecessary() Method search to find the enhanced aspects that need to be applied to the target object. Once found, create a proxy object through a dynamic proxy
    public Object getEarlyBeanReference(Object bean, String beanName) {
          
          
         Object cacheKey = getCacheKey(bean.getClass(), beanName);
         this.earlyProxyReferences.put(cacheKey, bean);
         return wrapIfNecessary(bean, beanName, cacheKey);
     }
     
     protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
          
          
         if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
          
          
             return bean;
         }
         if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
          
          
             return bean;
         }
         if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
          
          
             this.advisedBeans.put(cacheKey, Boolean.FALSE);
             return bean;
         }
    
         // Create proxy if we have advice.
         Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
         if (specificInterceptors != DO_NOT_PROXY) {
          
          
             this.advisedBeans.put(cacheKey, Boolean.TRUE);
             Object proxy = createProxy(
                     bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
             this.proxyTypes.put(cacheKey, proxy.getClass());
             return proxy;
         }
    
         this.advisedBeans.put(cacheKey, Boolean.FALSE);
         return bean;
     }
    
  4. After the execution of the above method is completed, the creation of object B is completed, and the process of creating object A is returned to the original process. At this time, the attributes of object A are filled in, and execution begins AbstractAutowireCapableBeanFactory#initializeBean() to perform enhancement processing on the target object. The core here is as follows:

    1. First call the AbstractAutowireCapableBeanFactory#invokeAwareMethods() method to execute the relevant introspection method of the target object and fill in specific attributes
    2. The call AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization() method traverses the post-processor list and executes the processor BeanPostProcessor#postProcessBeforeInitialization() method to perform pre-instantiation enhancements on the target object
    3. Call AbstractAutowireCapableBeanFactory#invokeInitMethods() method to determine whether the target object implements the InitializingBean interface, and if so, execute the corresponding interface method
    4. The callAbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization() method traverses the post-processor list and executes the processorBeanPostProcessor#postProcessAfterInitialization() method to perform post-instantiation enhancements on the target object. This step will trigger AbstractAutoProxyCreator#postProcessAfterInitialization() method creates a proxy for the target object
    protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
          
          
         if (System.getSecurityManager() != null) {
          
          
             AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
          
          
                 invokeAwareMethods(beanName, bean);
                 return null;
             }, getAccessControlContext());
         }
         else {
          
          
             invokeAwareMethods(beanName, bean);
         }
    
         Object wrappedBean = bean;
         if (mbd == null || !mbd.isSynthetic()) {
          
          
             wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
         }
    
         try {
          
          
             invokeInitMethods(beanName, wrappedBean, mbd);
         }
         catch (Throwable ex) {
          
          
             throw new BeanCreationException(
                     (mbd != null ? mbd.getResourceDescription() : null),
                     beanName, "Invocation of init method failed", ex);
         }
         if (mbd == null || !mbd.isSynthetic()) {
          
          
             wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
         }
    
         return wrappedBean;
     }
    
  5. AbstractAutoProxyCreator#postProcessAfterInitialization()The processing of the method is as follows. You can see that this part basically echoes the logic of step 3 of this section. If there is a target object tag in the cache of earlyProxyReferences, it proves that the target object has been created. Once the proxy object has been passed, there is no need to recreate it, thereby avoiding inconsistencies caused by the existence of multiple proxy objects

     public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
          
          
         if (bean != null) {
          
          
             Object cacheKey = getCacheKey(bean.getClass(), beanName);
             if (this.earlyProxyReferences.remove(cacheKey) != bean) {
          
          
                 return wrapIfNecessary(bean, beanName, cacheKey);
             }
         }
         return bean;
     }
    

3. Remaining issues

It can be seen from the source code analysis that the third-level cache mainly relies on AbstractAutoProxyCreator#earlyProxyReferences cache to solve the dynamic proxy problem in circular dependencies. However, it should be noted that when there is a special BeanPostProcessor that performs additional proxy enhancements on the target object,AbstractAutoProxyCreator#earlyProxyReferences the cache tag will become invalid, and a circular dependency exception will still occur

For example, in the circular dependency of A->B->A, if A is annotated by @Async and the asynchronous feature is enabled through @EnableAsync, it must Will throw circular dependency exception

Supongo que te gusta

Origin blog.csdn.net/weixin_45505313/article/details/131290961
Recomendado
Clasificación