This time, completely solve the circular dependency problem!

How to resolve circular dependencies in Spring must be a scenario where properties refer to each other in a single default singleton bean. For example, mutual references between several Beans:

or

setter mode prototype, prototype

The prototype scenario does not support circular dependencies, because the "prototype" scoped bean provides an instance for each bean request. The Spring container does not cache, so it cannot expose a created bean in advance, and it will throw abnormal.

Constructor parameter circular dependency

The Spring container will place each bean identifier being created in a "currently created Bean pool", and the Bean identifier will remain in this pool during the creation process.

Therefore, if you find that you are already in the "currently created Bean pool" during the process of creating a Bean, a BeanCurrentlyInCreationException will be thrown to indicate a circular dependency; and the created Bean will be removed from the "Currently Created Bean Pool".

The Spring container first creates a singleton A, A depends on B, then puts A in the "currently created Bean pool", then creates B, B depends on C, and then puts B in the "currently created Bean pool", and then creates C and C depend on A, but at this time A is already in the pool, so an error will be reported, because the beans in the pool are not initialized, so they will rely on the error, (the initialized Bean will be moved from the pool except).

Setter mode singleton, default mode

So in the default singleton attribute injection scenario, how does Spring support circular dependencies?

Since it is necessary to resolve circular dependencies, there must be dependencies. We assume that there are two classes:

A and B, A->B, B->A, and the two are injected into each other through @Autowired.

We assume that we start from A, that is, create the A object first, then create the B object, and then set the B object to the attributes of the A object through reflection (fieldB.set(a, b)), and find it when creating B It depends on A again. At this time, similarly, I want to find an A object set to the attribute of B, but there can only be one A object (singleton) in our system.

To solve this problem, need to save the A object when it was created earlier? We assume that it is stored in the cache. When the B object is to be used later, it will be OK to look it up in the cache first? !

Therefore, the way to solve the circular dependency is to save all the created objects. If there is a dependency when creating the object later, go to the cache to find it. If it is found, set it directly to the attribute of the object being created, and create a new one if it is not found. The object is given to the object being created and saved in the cache.

In fact, the same is true in Spring, but it has not only one cache, but four.

You can read this article for details.

https://juejin.im/post/6844903715602694152

Spring solves circular dependencies

First of all, Spring maintains three Maps internally, which is what we usually call the three-level cache.

In the DefaultSingletonBeanRegistry class of Spring, there are three Maps hanging above the class:

  • singletonObjects is our most familiar friend, commonly known as "singleton pool" and "container", which caches the place where singleton beans are created.
  • singletonFactories mapping to create the original factory of Bean
  • earlySingletonObjects maps the early references of the Bean, that is to say, the Bean in this Map is not complete, and it cannot even be called a "Bean", but an Instance.

The latter two Maps are actually at the "stepping stone" level. They are just used to help when creating a Bean, and it will be cleared after creation.

Why are the latter two Maps as a stepping stone? Suppose the Bean that is finally placed in singletonObjects is the cup of "cool and white" you want.

Then Spring has prepared two cups, namely singletonFactories and earlySingletonObjects, "pour" back and forth several times, dry the hot water into "cool white open" and put it in singletonObjects

  

The nature of circular dependence

Define two classes A and B:

public class A {
    private B b;

    public B getB() {
        return b;
    }

    public void setB(B b) {
        this.b = b;
    }

}
public class B {
    public A getA() {
        return a;
    }

    public void setA(A a) {
        this.a = a;
    }

    private A a;
}
public class CircularDependency { 
    private static Map<String, Object> cacheMap = new HashMap<>(2); 

    public static void main(String[] args) throws Exception { 
        // Pretend the scanned object 
        Class[] classes = {A. class, B.class }; 
        // pretend that the project is initialized and instantiate all beans 
        for (Class aClass: classes) { 
            getBean(aClass); 
        } 
        // check 
        System.out.println(getBean(B.class).getA() = = getBean(A.class)); 
        System.out.println(getBean(A.class).getB() == getBean(B.class)); 
    } 
    
    private static <T> T getBean(Class<T> beanClass) throws Exception{ 
        // This article uses simple lowercase class names to replace bean naming rules
        String beanName = beanClass.getSimpleName().toLowerCase(); 
        // If it is already a bean, return directly 
        if (cacheMap.containsKey(beanName)) { 
            return (T) cacheMap.get(beanName); 
        } 
        // Change the object Instantiate 
        Object object by itself = beanClass.getDeclaredConstructor().newInstance(); 
        // Put it in cache 
        cacheMap.put(beanName, object); 
        // Treat all fields as beans that need to be injected, create and inject into the current bean 
        Field[ ] fields = object.getClass().getDeclaredFields(); 
        for (Field field: fields) { 
            field.setAccessible(true); 
            // Get the class 
            Class of the field to be injected <?> fieldClass = field.getType();
            String fieldBeanName = fieldClass.getSimpleName().toLowerCase();  
            // If the bean to be injected is already in the cache Map, Then inject the value in the cache Map into the field
            // If the cache does not continue to create 
            field.set(object, cacheMap.containsKey(fieldBeanName)? CacheMap.get(fieldBeanName): getBean(fieldClass)); 
        } 
        // Property Filling is complete, 
        return (T) object; 
    } 
}

The effect of this code is that the circular dependency is actually processed, and after the processing is completed, the complete "Bean" is placed in the cacheMap.

Recommended viewing: Portal

If you think this article is helpful to you, you can like it and follow it to support it, or you can follow my public account, there are more technical dry goods articles and related information sharing, everyone can learn and progress together!

 

Guess you like

Origin blog.csdn.net/weixin_50205273/article/details/108652806