1. Preface
Spring's third-level cache and circular dependency are often heard by us, including interviewers, and interviewers will also ask what the third-level cache is? Why do I need a Level 3 cache? What is circular dependency? How does Spring solve circular dependencies? What kind of circular dependency does Spring not solve?
With the above questions, let’s take a closer look at the getBean() process of Spring BeanFactory; this article requires the reader to have a certain amount of Spring source code understanding;
2. Which three caches refer to
The third-level cache actually corresponds to three maps, which are used as member variables in the DefaultSingletonBeanRegistry class;
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { /** Cache of singleton factories: bean name to ObjectFactory. */ private Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); /** Cache of early singleton objects: bean name to bean instance. */ private Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16); /** Cache of singleton objects: bean name to bean instance. */ private Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); // ... }
- Level 3 cache singletonFactory: You can see that the ObjectFactory object is stored. Level 3 cache can create proxy objects in advance based on whether the object needs to create a proxy; or create a normal object;
- Secondary cache earlySingletonObjects: As the name suggests, it stores an early object, which stores semi-finished objects or proxy objects, which are used to solve the circular dependency problem in the object creation process; (Why is it said to be a semi-finished object here, because the properties of the objects stored here may not be completely injected);
- First-level cache singletonObjects: The finished object is stored here, and instantiation and initialization have been completed. The objects used in our project are all obtained in the first-level cache, and proxy objects and ordinary objects are stored in the first-level cache;
3. getBean() process
Let’s take a look at the entire process of Level 3 cache through getBeanFactory’s getBean();
Go directly to getBean() in AbstractBeanFactory;
// --------------------------- AbstractBeanFactory --------------------------- public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } // --------------------------- AbstractBeanFactory --------------------------- protected <T> T doGetBean( String name, Class<T> requiredType, Object[] args, boolean typeCheckOnly) { // ... // Let's look at the main process directly // Create bean instance. if (()) { // Call DefaultSingletonBeanRegistry#getSingleton() here // Parameter 1 is beanName // Parameter 2 is an ObjectFactory functional object sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; } }); beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // ... }
DefaultSingletonBeanRegistry#getSingleton() is called in AbstractBeanFactory#doGetBean(), and the second parameter of this getSingleton() is an ObjectFactory functional object. The implementation logic of this functional object is return createBean();
Let's first look at DefaultSingletonBeanRegistry#getSingleton();
// ------------------------ DefaultSingletonBeanRegistry ----------------------- public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { synchronized () { // 1. Get it from the first level cache first // If the bean object is obtained, return it directly // If the bean object is not obtained, enter the subsequent bean object creation process Object singletonObject = (beanName); if (singletonObject == null) { if () { throw new BeanCreationNotAllowedException(); } // ... try { // 2. Call getObject() of the ObjectFactory object to create a bean object // From the above analysis we know that the final implementation is return createBean() // We need to look at the createBean() process singletonObject = (); newSingleton = true; } catch (IllegalStateException ex) { singletonObject = (beanName); if (singletonObject == null) { throw ex; } } finally { afterSingletonCreation(beanName); } if (newSingleton) { // 3. When the bean is created successfully // Put the bean object into the first-level cache singletonObjects // and remove the corresponding value of beanName from the secondary cache and the third cache addSingleton(beanName, singletonObject); } } return singletonObject; } } // ------------------------ DefaultSingletonBeanRegistry ----------------------- protected void addSingleton(String beanName, Object singletonObject) { synchronized () { // Put the bean object into the first-level cache // and remove the corresponding value of beanName from the secondary cache and the third cache (beanName, singletonObject); (beanName); (beanName); (beanName); } }
The core implementation is the createBean() of ObjectFactory. Let's look at the createBean() logic;
// --------------------- AbstractAutowireCapableBeanFactory ------------------- protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) { RootBeanDefinition mbdToUse = mbd; // ... try { // Call doCreateBean() to create a bean object and return the bean object Object beanInstance = doCreateBean(beanName, mbdToUse, args); return beanInstance; } catch (Throwable ex) { throw new BeanCreationException(); } } // --------------------- AbstractAutowireCapableBeanFactory ------------------- protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException { BeanWrapper instanceWrapper = null; if (()) { instanceWrapper = (beanName); } if (instanceWrapper == null) { // 1. Instantiate the object // Construct an instance bean object according to the appropriate construction method instanceWrapper = createBeanInstance(beanName, mbd, args); } Object bean = (); Class<?> beanType = (); if (beanType != ) { = beanType; } // 2. Should I use Level 3 cache? Generally, I will use Level 3 cache boolean earlySingletonExposure = (() && && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { // 3. Put beanName and the corresponding functional object ObjectFactory into the Level 3 cache // The ObjectFactory has already obtained the bean object that has just been instantiated, but only the constructor has been executed // The getObject() implementation of the ObjectFactory is to call getEarlyBeanReference() addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // 4. Initialize the Bean object Object exposedObject = bean; try { // 4.1 Attribute injection // If A depends on B, getBean(A) will be adjusted when getBean(B) // If A and B have circular dependencies, getBean(A) -> getBean(B) -> getBean(A) will occur populateBean(beanName, mbd, instanceWrapper); // 4.2 Initialize the bean object // Here we will execute some post-processing methods of BeanPostProcessor // The Spring AOP we are familiar with is the proxy class object generated here // For example, the postprocessor used by @Transactional is AbstractAutoProxyCreator // For example, the postprocessor used by @Async is AbstractAdvisingBeanPostProcessor // Although both generate AOP objects, the processing logic of these two is different when dealing with circular dependencies. I will explain in detail later exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { throw ex; } if (earlySingletonExposure) { // 5. Decide whether getBean(A) returns a bean object or throws an exception // Parameter 2 is false // Get bean objects from the first-level cache or the second-level cache // When you get here, you usually try to get bean objects from the secondary cache // This is more complicated, let's talk about it later Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (! && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(); for (String dependentBean : dependentBeans) { if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { (dependentBean); } } if (!()) { throw new BeanCurrentlyInCreationException(); } } } } // 6. Register to delete bean logic try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException(); } // 7. Return the bean object return exposedObject; }
This is the article about Spring Level 3 cache and circular dependence. For more related content about Spring Level 3 cache and circular dependence, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!