General documentation: Article directory Github: github.com/black-ant

A. The preface

This is a tentative article, SpringIOC’s notes have been basically put together, but never quite figured out how to output them as documentation.

In my opinion, if I take the time to put together a document, then no matter how long it takes, even if I forget something, when I read it again, I should be able to quickly understand it. The whole IOC system will be exported in this way.

The whole IOC system is so huge that even after combing through all the previous notes, I still feel that it is not enough. But let’s pick something nice, see if we can sort this out first.

2. Functional phenomenon

A circular dependency is when three objects depend on each other, and when getBean is used to obtain the dependent Bean, a circular dependency is formed



// Use Scope to try the Prototype mode
@Component
@Scope(value = "prototype")
public class BeanCService{}

// When BeanA, BeanB, and BeanC are interdependent, the result looks like this:
 The dependencies of some of the beans in the application context form a cycle:
   beanStartService (field privateCom. Gang. BeanAService com. Gang. BeanStartService. BeanAService) ┌ ─ ─ ─ ─ ─ ┐ | BeanAService (fieldprivateCom. Gang. BeanBService com. Gang. BeanAService. BeanBService) write left | BeanBService (fieldprivateCom. Gang. BeanCService com. Gang. BeanBService. BeanCService) write left | BeanCService (fieldprivateCom. Gang. BeanAService com. Gang. Study. BeanCService. BeanAService) └ ─ ─ ─ ─ ─ ┘Copy the code

With @scope (value = “prototype”) removed, all normal loop dependencies are handled in singleton mode

3. Source tracking

From the point of view of source code analysis, SpringIOC singleton is how to control the cyclic dependency

3.1 Loading process of singleton Beans

Where it all started

The starting point, of course, is AbstractBeanFactory, a step that will be discussed in detail at Bean creation time, but will be spared here

C-abstractbeanfactory m-DogetBean: -object sharedInstance = getSingleton(beanName) : getBean from cacheCopy the code

As you can see, when creating the first BeanA into DefaultSingletonBeanRegistry

/ / further DefaultSingletonBeanRegistry, which has the following parameters
C180- DefaultSingletonBeanRegistry
    F01- private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); ? - Singleton cache: Bean to bean instance name F02-private finalMap<String, ObjectFactory<? >> singletonFactories =new HashMap<>(16); ? - Singleton factory cache :ObjectFactory bean name F03-private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); ? - Early singleton cache: Bean to bean instance name F04-private final Set<String> registeredSingletons = new LinkedHashSet<>(256); ? - A set of registered singletons containing the bean name F05- in the order they were registeredprivate final Set<String> singletonsCurrentlyInCreation =Collections.newSetFromMap(new ConcurrentHashMap<>(16)); ? - Name of the bean being created F06-private final Set<String> inCreationCheckExclusions =Collections.newSetFromMap(new ConcurrentHashMap<>(16)); ? - Name of the bean currently excluded from the create check F07-privateSet<Exception> suppressedExceptions; ? - Exception list F08-private boolean singletonsCurrentlyInDestruction = false; ? - Indicates whether singleton F09- is being destroyedprivate final Map<String, Object> disposableBeans = newLinkedHashMap<>(); ? - One-off Bean instance: from the bean name to the one-off instance F10-private final Map<String, Set<String>> containedBeanMap = new ConcurrentHashMap<>(16); ? - Contains mapping between bean names: the bean name to the group of bean names contained by the bean F11-private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64); ? - Mapping between dependent bean names :bean name to a set of dependent bean names F12-private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64); ? - Mapping between dependent Bean names: the bean name to a set of bean names for bean dependenciesCopy the code

How do I create a singleton Bean

// When we create a singleton Bean
C180- DefaultSingletonBeanRegistry      
    M180_01- getSingleton(String beanName, booleanAllowEarlyReference) - first from Map<String, Object> singletonObjects - if not, and is creating (->M180_02), Continue from earlySingletonObjects - if there is still none and you need to create an early reference, continue fromthis. SingletonFactories - If singletonFactories get it, The current bean into singletonObjects and removed from singletonFactories M180_02 - isSingletonCurrentlyInCreation? - determine whether the current Bean has been registered - singletonsCurrentlyInCreation. The contains (beanName)// M180_01 code: obtain the corresponding beans from the three concurrenthashMaps
    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;
    }

Copy the code

The main part of the process is to get Bean instances from the three CurrentHashMap local caches

When the first Bean is created, it will always be in the cache, even if it is not fully loaded

BeanB->> BeanB->>BeanC: Get(C) BeanC BeanC-->>BeanA: Get(A) getBeana BeanC->> singletonObjects: getBeana BeanC->> BeanC BeanC->> earlySingletonObjects: BeanC->> earlySingletonObjects: Getbeana earlySingletonObjects->> BeanC->> singletonFactories: GetBeana earlySingletonObjects->> BeanC->> singletonFactories: GetBeana earlySingletonObjects->> BeanC->> singletonFactories: GetBeana ()

Let’s take a closer look at these caches:

So let’s scroll down and see when did these two objects go up

// Let's review the above objectsSingletonsCurrentlyInCreation: set the current Bean name is creating singletonObjects: singleton object cache earlySingletonObjects: SingletonFactories, the cache of an early singleton object: the cache of a singleton factoryCopy the code

3.2 singletonsCurrentlyInCreation

Step 1: singletonsCurrentlyInCreation into place

C180- DefaultSingletonBeanRegistry M180_03- beforeSingletonCreation ? -this. SingletonsCurrentlyInCreation. Add (beanName) : add the current beanName M180_04 - afterSingletonCreation? -this. SingletonsCurrentlyInCreation. Remove (beanName) : remove the current beanName// This should give you an idea of how to insert it
// PS: beforeSingletonCreation (beforeSingletonCreation) : beforeSingletonCreation (beforeSingletonCreation) : beforeSingletonCreation (beforeSingletonCreation)C- AbstractAutowireCapableBeanFactory M- getSingletonFactoryBeanForTypeCheck C- DefaultSingletonBeanRegistry M- GetSingleton: singleton beans to create the position of the C - FactoryBeanRegistrySupport M - getObjectFromFactoryBeanCopy the code

3.3 Storage of singletonObjects

The location where singletonObjects are stored

M180_05- addSingleton
    	- this.singletonObjects.put(beanName, singletonObject) ? M- getSingletonMutex: This is the only possible place to put datareturn this.singletonObjects;

// Break point trace found the call made in the following class:C- FactoryBeanRegistrySupport M- getObjectFromFactoryBean : Just as a lock object - AbstractApplicationEventMulticaster M - setBeanFactory - Cthis.retrievalMutex = this. The beanFactory. GetSingletonMutex () : the objects for reference? - Most of them are also lock objects and do not operateCopy the code

PS: Using this object as a synchronized monitor effectively guarantees uniqueness

At first I was thinking that ConcurrentHashMap should also guarantee concurrent uniqueness, but later I thought that the uniqueness here should be unique on the business process. For example, if I delete here, you can’t add there, and when I’m done, you can continue

SingletonObjects deleted place

C180 - DefaultSingletonBeanRegistry M180_07 clearSingletonCache - more than a collection, here is all set to empty? M180_08- removeSingleton will be called in the destroySingletons process when removing a single singletonCopy the code

PS: That is, when the singleton is destroyed, the data is emptied or removed

/ / M180_07 code
protected void clearSingletonCache(a) {
    synchronized (this.singletonObjects) {
        this.singletonObjects.clear();
        this.singletonFactories.clear();
        this.earlySingletonObjects.clear();
        this.registeredSingletons.clear();
        this.singletonsCurrentlyInDestruction = false; }}Copy the code
SequenceDiagram addSingleton->> singletonObjects: ClearSingletonCache -->>singletonObjects: Remove removeSingleton from clearSingletonCache -->>singletonObjects: Removed from removeSingleton

3.4 earlySingletonObjects

Where earlySingletonObjects are stored

C180- DefaultSingletonBeanRegistry  
	M180_01- getSingleton(String beanName, booleanallowEarlyReference) ? - is where we got it before, and you can see that there's an allowEarlyReference - herethis.earlySingletonObjects.put(beanName, singletonObject)
Copy the code

PS: The location of this is when the initial BeanA class is loaded, no dependencies are injected at this time, just the first build

EarlySingletonObjects is an intermediate cache that will be cleared after the Single Bean is added

EarlySingletonObjects clear place

C180- DefaultSingletonBeanRegistry M180_09- addSingletonFactory(String beanName, ObjectFactory<? > singletonFactory) : clear a single M180_07- clearSingletonCache: clear all M180_05- addSingleton: clear a single M180_07Copy the code
SequenceDiagram getSingleton->> earlySingletonObjects: getSingleton EarlySingletonObjects collection addSingletonFactory-->>earlySingletonObjects: Remove clearSingletonCache-->>earlySingletonObjects from the addSingletonFactory method: Clear addSingleton-->>earlySingletonObjects: removed from the clearSingletonCache method

3.5 singletonFactories

SingletonFactories are singletons

// Let's seeC180- DefaultSingletonBeanRegistry M180_07- addSingletonFactory(String beanName, ObjectFactory<? > singletonFactory) IF- IF singletonObjects (F01) contains the current BeanName TRUE- singletonFactories (F02) adds the current Bean and singletonFactory ? -this.singletonFactories.put(beanName, singletonFactory);

// PS: which part of the singleton factory is calledFirst, getSingle, meet is actually a lambda expressions C180 - DefaultSingletonBeanRegistry M180_01 - getSingleton (String beanName,booleanallowEarlyReference) - ObjectFactory<? > singletonFactory =this.singletonFactories.get(beanName);
		- singletonObject = singletonFactory.getObject();

/ / the object is generated in the AbstractAutowireCapableBeanFactory # doCreateBean
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

// A getEarlyBeanReference method is passed, which is called back to getObject
() -> getEarlyBeanReference(beanName, mbd, bean) --- singletonFactory.getObject() 
/ / will ultimately generated by SmartInstantiationAwareBeanPostProcessor
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);    

// Finally, an empty object is generated and put in

Copy the code

PS: The object will be picked up by getSingleton in a subsequent process

sequenceDiagram DefaultSingletonBeanRegistry->> singletonFactories : Note left of singletonFactories: SingletonFactories collection getSingleton-->>singletonFactories: Remove removeSingleton from singletonFactories -->>singletonFactories: Remove clearSingletonCache in the removeSingleton method -->>singletonFactories: Clear in the clearSingletonCache method

3.6 Why Prototype is not feasible

Here’s how Prototype loads:

Prototype creation is done in AbstractBeanFactory

C171- AbstractBeanFactory 
    M171_02- doGetBean( String name, Class<T> requiredType,Object[] args, booleanTypeCheckOnly) - If it is a Singleton, then getSingleton? If Prototype, then createBean? - There is no cache class to handle this// M171_02 pseudo-code
if (mbd.isSingleton()) {
    / /...
} else if (mbd.isPrototype()) {
    Object prototypeInstance = null;
    try {
        beforePrototypeCreation(beanName);
        prototypeInstance = createBean(beanName, mbd, args);
    }finally {
        afterPrototypeCreation(beanName);
    }
    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}


Copy the code

Complete the process

BeanC->> BeanC->> BeanC->> BeanC BeanC->> earlySingletonObjects: BeanC->> earlySingletonObjects: Getbeana earlySingletonObjects->> BeanC->> singletonFactories: GetBeana earlySingletonObjects->> BeanC->> singletonFactories: GetBeana earlySingletonObjects->> BeanC->> singletonFactories: GetBeana ()

Supplementary information: SingletonBeanRegistry system

conclusion

The process of dealing with circular dependencies is simple and short, consisting mainly of the use of several cache collections.

The main purpose of this article is to see if we can make the document with fast meeting capability

reference

cmsblogs.com/?cat=206

www.cnblogs.com/qinzj/p/114…

The appendix

Found a very good picture, here to share:

The original @ www.cnblogs.com/qinzj/p/114…