be based on Spring-5.1.5.RELEASE
problem

We all know Spring Three level cache is used to solve the problem of circular dependency . But is it necessary to have three levels of cache to solve , Can't L2 cache solve this problem ?
To analyze whether one level of cache can be removed , Just go through it first Spring How to solve the circular dependency through three-level cache .

Cyclic dependence

So called circular dependence , It's two or more bean Depend on each other , Finally, a closed loop is formed
. such as “A Object dependency B object , and B Objects are also dependent A object ”, perhaps “A Object dependency B object ,B Object dependency C object ,C Object dependency A object ”; Similar to the following code :
public class A { private B b; } public class B { private A a; }
Under normal circumstances , The following will happen :

* Created by building functions A object (A The object is semi-finished products , Properties and calls have not been injected init method ).
* A Object needs to be injected B object , Discovery object pool ( cache ) Not yet B object ( After the object is created and the properties are injected and initialized , Will be put into the object cache ).
* Created by building functions B object (B The object is semi-finished products , Properties and calls have not been injected init method ).
* B Object needs to be injected A object , We found that there is no object pool yet A object .
* establish A object , Cycle the above steps .
Level 3 cache

Spring The core idea of solving circular dependence is to expose in advance :

* Created by building functions A object (A The object is semi-finished products , Properties and calls have not been injected init method ).
* A Object needs to be injected B object , I found that it hasn't been in the cache yet B object , Will semi-finished object A Put in semi finished product cache .
* Created by building functions B object (B The object is semi-finished products , Properties and calls have not been injected init method ).
* B Object needs to be injected A object , Retrieve the semi-finished object from the semi-finished product cache A.
* B Objects continue to inject other properties and initializations , After that, the finished product will be finished B The object is put into the finished product cache .
* A Object continues to inject properties , Get finished product from finished product cache B Object and inject .
* A Objects continue to inject other properties and initializations , After that, the finished product will be finished A The object is put into the finished product cache .
There are three levels of cache :
/** 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);
Cache description
singletonObjects First level cache , Storage of usable finished products Bean.
earlySingletonObjects Second level cache , Storage of semi-finished products Bean, Semi finished Bean Is the object created , However, properties and initialization are not injected . To solve the circular dependence .
singletonFactories Level 3 cache , What's in it Bean Factory object , Used to generate semi-finished products Bean And put it into the secondary cache . To solve the circular dependence .
Understand the principle , The best way is to read the source code , Create from Bean Methods AbstractAutowireCapableBeanFactor.doCreateBean start with .

1. In the construction Bean After object , Expose objects to cache ahead of time , At this time, the exposed object is only constructed , Properties and initialization have not been injected .
public abstract class AbstractAutowireCapableBeanFactory extends
AbstractBeanFactory implements AutowireCapableBeanFactory { protected Object
doCreateBean(final String beanName, final RootBeanDefinition mbd, final
@Nullable Object[] args) throws BeanCreationException { …… // Is it exposed in advance 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)); } …… } }
2. Pre exposed objects are placed Map<String, ObjectFactory<?>> singletonFactories In cache , This is not a direct Bean
Put into cache , It's packaged into ObjectFactory Object and then put .
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry
implements SingletonBeanRegistry { protected void addSingletonFactory(String
beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory,
"Singleton factory must not be null"); synchronized (this.singletonObjects) {
// First level cache if (!this.singletonObjects.containsKey(beanName)) { // Level 3 cache
this.singletonFactories.put(beanName, singletonFactory); // L2 cache
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName); } } } } public interface
ObjectFactory<T> { T getObject() throws BeansException; }
3. Why a layer of packaging ObjectFactory object ?

If the Bean There are corresponding agents , When other objects are injected , The corresponding proxy object should be injected ; however Spring There is no way to know in advance whether the object has a circular dependency , And normally ( No,
Cyclic dependence ),Spring They are creating finished products Bean After that, the corresponding proxy is created . At this time Spring There are two options :

* With or without circular dependence , Create proxy objects in advance , And put the proxy object into the cache , When a circular dependency occurs , Other objects can get proxy objects directly and inject them .
* Do not create proxy objects in advance , When a circular dependency is injected by other objects , The proxy object is generated in real time . In this way, in the case of no cyclic dependency ,Bean You can press it Spring Design principles
To create a .
Spring The second way was chosen , How to expose objects in advance without generating agents ?
Spring It's a layer on the outside of the object ObjectFactory, What was exposed in advance was ObjectFactory object , Only when it is injected
ObjectFactory.getObject Real time generation of proxy objects in mode , And put the generated proxy object into the second level cache Map<String, Object>
earlySingletonObjects.
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd,
bean));:
public abstract class AbstractAutowireCapableBeanFactory extends
AbstractBeanFactory implements AutowireCapableBeanFactory { 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;
} }
In order to prevent the object from being initialized later (init) Repeat agent when , When creating an agent ,earlyProxyReferences The cache records the proxied objects .
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware { private
final Map<Object, Object> earlyProxyReferences = new ConcurrentHashMap<>(16);
@Override public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean); return wrapIfNecessary(bean,
beanName, cacheKey); } }
4. Injection properties and initialization

After early exposure :

* adopt populateBean Method injection properties , Injection of other Bean Object time , Will go to the cache first , If the cache does not , The object is created and injected .
* adopt initializeBean Method initializes the object , Contains the creation agent . public abstract class
AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements
AutowireCapableBeanFactory { protected Object doCreateBean(final String
beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws
BeanCreationException { …… // 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); }
} …… } } // Gets the object to be injected public class DefaultSingletonBeanRegistry extends
SimpleAliasRegistry implements SingletonBeanRegistry { protected Object
getSingleton(String beanName, boolean allowEarlyReference) { // First level cache Object
singletonObject = this.singletonObjects.get(beanName); if (singletonObject ==
null && isSingletonCurrentlyInCreation(beanName)) { synchronized
(this.singletonObjects) { // L2 cache singletonObject =
this.earlySingletonObjects.get(beanName); if (singletonObject == null &&
allowEarlyReference) { // Level 3 cache ObjectFactory<?> singletonFactory =
this.singletonFactories.get(beanName); if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName); } } } } return singletonObject; } }
5. Put the singleton cache that has been created

After going through the following steps , Finally passed addSingleton Method will eventually generate the available Bean Put it into the singleton cache .

* AbstractBeanFactory.doGetBean ->
* DefaultSingletonBeanRegistry.getSingleton ->
* AbstractAutowireCapableBeanFactory.createBean ->
* AbstractAutowireCapableBeanFactory.doCreateBean ->
* DefaultSingletonBeanRegistry.addSingleton public class
DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
SingletonBeanRegistry { /** Cache of singleton objects: bean name to bean
instance. */ private final Map<String, Object> singletonObjects = new
ConcurrentHashMap<>(256); /** Cache of singleton factories: bean name to
ObjectFactory. */ private final Map<String, ObjectFactory<?>>
singletonFactories = new HashMap<>(16); /** Cache of early singleton objects:
bean name to bean instance. */ private final Map<String, Object>
earlySingletonObjects = new HashMap<>(16); 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); } } }
L2 cache

Step three above 《 Why a layer of packaging ObjectFactory object ?》 There are two options :

* With or without circular dependence , Create proxy objects in advance , And put the proxy object into the cache , When a circular dependency occurs , Other objects can get proxy objects directly and inject them .
* Do not create proxy objects in advance , When a circular dependency is injected by other objects , The proxy object is generated in real time . In this way, in the case of no cyclic dependency ,Bean You can press it Spring Design principles
To create a .
Sping The second one was chosen , If it's the first one , There will be different processing logic as follows :

* When semi-finished products are exposed in advance , Direct execution getEarlyBeanReference Create to agent , And put it into the cache earlySingletonObjects in .
* With the last step , Then you don't have to go through ObjectFactory To delay execution getEarlyBeanReference, You don't need to singletonFactories
This level of cache .
Is this treatment feasible ?
Here's an experiment , Yes AbstractAutowireCapableBeanFactory Make a little transformation , Immediately after it is put into the third level cache, it is taken out and put into the second level cache , This is a three-level cache
It's completely ignored , It's equivalent to having only a second level cache .

public abstract class AbstractAutowireCapableBeanFactory extends
AbstractBeanFactory implements AutowireCapableBeanFactory { protected Object
doCreateBean(final String beanName, final RootBeanDefinition mbd, final
@Nullable Object[] args) throws BeanCreationException { …… // Is it exposed in advance 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)); // Immediately from the third level cache into the second level cache getSingleton(beanName, true); } …… } }
The test results are OK , And from the source analysis can be concluded that the performance of the two methods is the same , It doesn't affect it Sping Starting speed . So why Sping Do not select L2 cache mode , It's an extra layer of caching ?
If you want to use L2 cache to resolve circular dependency , signify Bean The proxy object is created after construction , That's against it Spring Design principles .Spring combination AOP Follow Bean Life cycle of , It's in
Bean After the creation is complete, the AnnotationAwareAspectJAutoProxyCreator This is the post processor , In this post-processing
postProcessAfterInitialization After initialization, the Bean complete AOP agent . If there is a cyclic dependency
, There is no way , Only give Bean Create the agent first , But there is no circular dependency , The beginning of design is to let Bean The agent is completed at the last step of the lifecycle, rather than immediately after instantiation .

Technology
©2019-2020 Toolsou All rights reserved,
vue Changes in data Interface not updated Python realization js And Django Front and back interaction in the background JavaSwing To achieve a simple Lianliankan games C# Making a simplified version of calculator elementui Shuttle box el-transfer Display list content text too long JVM summary Regression of dependent variable order categories (R language )【C#】 The realization of student achievement information management system use css Design a simple style html login interface C language --------- Tetris ( source code )