Creation is not easy, reprint please indicate the author: gold @ small xi son + source link ~

If you want to learn more about Spring source code, click to go to the rest of the line-by-line parsing Spring series

One, foreword

The IOC part of Spring is almost finished; the next article will start with the AOP source code. The purpose of this post is to share a little dessert for students about the FactoryBean interface.

This interface is not often used in our daily development, but more often used when third-party frameworks connect to Spring. However, since this interface is similar to the BeanFactory which carries the main logic in our IOC, the interviewer will occasionally ask about the difference between the two during the interview.

I’d say it’s a big difference, because they’re basically unrelated, they’re two different things.

Second,FactoryBeanThe role of

Before we get into the principles, let’s briefly talk about what the FactoryBean interface does. Let’s first look at the definition of FactoryBean:

public interface FactoryBean<T> {
	@Nullable
	T getObject(a) throws Exception;

	@NullableClass<? > getObjectType();FactoryBean#getObject returns whether the bean instance is a singleton
    FactoryBean#getObject will only be called once if it is a singleton
	default boolean isSingleton(a) {
		return true; }}Copy the code

Then we write a class that implements this interface:

public class SubBean {}@Service
public class FactoryBeanDemo implements FactoryBean<SubBean> {
    @Override
    public SubBean getObject(a) throws Exception {
        return new SubBean();
    }
    @Override
    publicClass<? > getObjectType() {returnSubBean.class; }}Copy the code

Let’s launch Spring and print out the factoryBeanDemo:

public void test(a) {
    applicationContext = new AnnotationConfigApplicationContext("com.xiaoxizi.spring");
    Object subBean = applicationContext.getBean("factoryBeanDemo");
    System.out.println(subBean);
}
Copy the code

Output:

com.xiaoxizi.spring.factoryBean.SubBean@3e0e1046
Copy the code

As you can see, we actually get an instance of FactoryB via getBean(“factoryBeanDemo”), not the instance of factoryBeanDemo that we tagged with the @Service annotation.

That’s what the FactoryBean interface is for, when we register a FactoryBean with Spring, BeanName will be the instance of the subBean (we use subbeans to represent the returned object of FactoryBean#getObject) returned by FactoryBean#getObject. And notice the FactoryBean#isSingleton method to show that we can also specify whether the instances obtained by the getObject method are singletons or multiples.

So, in this case, can we still get an instance of FactoryBeanDemo? Of course, we can, but we need to make a little change:

public void test(a) {
    applicationContext = new AnnotationConfigApplicationContext("com.xiaoxizi.spring");
    Object subBean = applicationContext.getBean("factoryBeanDemo");
    System.out.println(subBean);
    
    Object factoryBeanDemo = applicationContext.getBean("&factoryBeanDemo");
    System.out.println(factoryBeanDemo);
}
Copy the code

Output:

com.xiaoxizi.spring.factoryBean.SubBean@3e0e1046
com.xiaoxizi.spring.factoryBean.FactoryBeanDemo@24c1b2d2
Copy the code

That is, if you normally fetch a subBean instance from the Spring container using beanName, you can only get the subBean instance, but if you precede beanName with an & and use &beanname from the Spring container, you can get the FactoryBean instance itself.

Three, source code analysis

So how does Spring support FactoryBean’s capabilities? Let’s look at the source code together. When we talked about bean life cycles, we talked about singleton beans that are initialized when the Spring container starts. So for a FactoryBean instance, its FactoryBean#getObject method is also initialized when the Spring container starts. Where are subBean instances stored? With that in mind, let’s take a look at the AbstractBeanFactory#doGetBean method that is the core logic for getting a bean:

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
                          @Nullable final Object[] args, boolean typeCheckOnly) {
	// Convert the beanName that needs to be fetched
    final String beanName = transformedBeanName(name);
    Object bean;

    // Get the singleton object directly from the level-1 cache
    Object sharedInstance = getSingleton(beanName);
    if(sharedInstance ! =null && args == null) {
        // Get the final returned bean instance. FactoryBean's main processing logic is here
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    } else {
        // skip...
        if (mbd.isSingleton()) {
            // The spring container will go to this branch when it starts
            // Triggers the initialization process for the current bean
            sharedInstance = getSingleton(beanName, () -> {
                try {
                    return createBean(beanName, mbd, args);
                }
                catch (BeansException ex) {
                    destroySingleton(beanName);
                    throwex; }});// After initializing the singleton bean and getting the bean object, this method will eventually be called as well
            // Get the final returned bean instance. FactoryBean's main processing logic is here
            BeanDefinition = BeanDefinition = BeanDefinition = BeanDefinition = BeanDefinition = BeanDefinition
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
        } 
        else if (mbd.isPrototype()) {
            // Examples of beans
            Object prototypeInstance = null;
            try {
                beforePrototypeCreation(beanName);
                prototypeInstance = createBean(beanName, mbd, args);
            }
            finally {
                afterPrototypeCreation(beanName);
            }
            // This is also called when there are multiple examples
            BeanDefinition = BeanDefinition = BeanDefinition = BeanDefinition = BeanDefinition = BeanDefinition
            bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
        }else {
            // Other custom scope
            String scopeName = mbd.getScope();
            final Scope scope = this.scopes.get(scopeName);
            if (scope == null) {
                throw newIllegalStateException(..) ; }try {
                Object scopedInstance = scope.get(beanName, () -> {
                    beforePrototypeCreation(beanName);
                    try {
                        return createBean(beanName, mbd, args);
                    }
                    finally{ afterPrototypeCreation(beanName); }});// This method is called last
                BeanDefinition = BeanDefinition = BeanDefinition = BeanDefinition = BeanDefinition = BeanDefinition
                bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
            }
            catch (IllegalStateException ex) {
                throw newBeanCreationException(...) ; } } } } }Copy the code

And we can see that either the initialization or the

1. transformedBeanNameTo deal with&symbol

You can get an instance of factoryBean using getBean(“&factoryBeanDemo”), so spring does the initial processing for this & in transformedBeanName:

// AbstractBeanFactory#transformedBeanName
protected String transformedBeanName(String name) {
    CanonicalName = beanName; // canonicalName = beanName
    // Alias names are rarely used
    / / the main see BeanFactoryUtils transformedBeanName
    return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}
// BeanFactoryUtils#transformedBeanName
public static String transformedBeanName(String name) {
    . / / the way, first this the BeanFactory FACTORY_BEAN_PREFIX constants is the & symbol
    if(! name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {// If it doesn't start with an ampersand, it returns
        return name;
    }
    return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
        // Kill all the ampersand that precedes the name
        FactoryBean --> factoryBean --> factoryBean
        do {
            beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
        }
        while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
        return beanName;
    });
}
Copy the code

That is, for our call to getBean(“&factoryBeanDemo”), after transformedBeanName(name), the beanName returned is “factoryBeanDemo”.

2. getObjectForBeanInstanceGet what you eventually need to returnbeanThe instance

When getBean is called, it triggers the creation and initialization of the bean process (singleton container initialization/multiple cases create bean instances on each call), Whether we get the singleton directly from the level 1 cache or not, we eventually need to use the obtained bean instance to call getObjectForBeanInstance to get the bean that we finally need to return. This is where our FactoryBean logic is handled:

// Note that the name and beanName values are passed in
// Name is the original value before transformedBeanName, which is passed in when we call the getBean method
// beanName is the converted beanName. Normally (name does not precede &), the two are the same
// If MBD is not empty, the bean object has just been initialized
protected Object getObjectForBeanInstance(
    Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
    if (BeanFactoryUtils.isFactoryDereference(name)) {
        // If the name is prefixed with &, we want to get an instance of factoryBean
        // instead of getting the instance returned by factoryBean#getObject
        if (beanInstance instanceof NullBean) {
            return beanInstance;
        }
        // If you get a bean instance from &xxx, then the bean instance you get must implement the FactoryBean interface
        // beanName is user specified. // beanName is user specified
        // If the user specifies a bean name that is &xxx but does not actually implement FactoryBean, it is not allowed
        // An error will be reported on startup
        if(! (beanInstanceinstanceof FactoryBean)) {
            throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
        }
        if(mbd ! =null) {
            mbd.isFactoryBean = true;
        }
        // Return the factoryBean instance directly
        // This is why getBean("&factoryBeanDemo") gets an instance of factoryBean
        return beanInstance;
    }
	
    // This is a normal name that does not begin with &
    if(! (beanInstanceinstanceof FactoryBean)) {
        // At this point, if the obtained bean instance does not implement the FactoryBean interface,
        // There is no special processing, just return it
        // For normal beans (those that do not implement FactoryBean), it is returned here
        return beanInstance;
    }
	
    Object object = null;
    if(mbd ! =null) {
        // If MBD is not empty, the Bean object (FactoryBean) has just been initialized
        mbd.isFactoryBean = true;
    }
    else {
        // The non-bean object (FactoryBean) was just initialized and fetched directly from the cache
        object = getCachedObjectForFactoryBean(beanName);
    }
    if (object == null) {
        // If there is no subBean in the cache for this factoryBean
        // Or when factoryBean has just been initializedFactoryBean<? > factory = (FactoryBean<? >) beanInstance;if (mbd == null && containsBeanDefinition(beanName)) {
            mbd = getMergedLocalBeanDefinition(beanName);
        }
        booleansynthetic = (mbd ! =null && mbd.isSynthetic());
        // Get the subBean from factoryBean and return itobject = getObjectFromFactoryBean(factory, beanName, ! synthetic); }// subBean is returned
    return object;
}
Copy the code

As you can see, if an instance of A is a factoryBean, when we call getBean(“a”), it is factoryBean#getObject that creates an instance of A and triggers it to get the subBean instance and return it; If you use getBean(“&a”), you just instantiate an instance of A and return factoryBean itself.

2.1. getCachedObjectForFactoryBeanGet from cachesubBean

As you can see, when the last parameter BeanDefinition of the getObjectForBeanInstance method is empty, it means that a factoryBean instance has been created, This time will pass getCachedObjectForFactoryBean method try to directly from the cache access subBean object, the method of logic is simple:

/ / the current class is FactoryBeanRegistrySupport
private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>(16);
protected Object getCachedObjectForFactoryBean(String beanName) {
    // Take it directly from the cache
    return this.factoryBeanObjectCache.get(beanName);
}
Copy the code

If there is a subBean instance in the cache, it simply returns that instance. If there is no subBean instance in the cache, it continues through the following logic to get the subBean.

2.2. getObjectFromFactoryBeanfromfactoryBeanTo obtainsubBean

Assuming there are no subBean instances in the cache, Ken will eventually go to the getObjectFromFactoryBean method to get a subBean object:

protected Object getObjectFromFactoryBean(FactoryBean<? > factory, String beanName,boolean shouldPostProcess) {
    // note that the isSingleton is FactoryBean#isSingleton
    FactoryBean is a singleton -containsSingleton(beanName),
    // The subBean is cached only when the subBean is defined as a singleton
    if (factory.isSingleton() && containsSingleton(beanName)) {
        synchronized (getSingletonMutex()) {
            / / lock
            // Take it from the cache first
            Object object = this.factoryBeanObjectCache.get(beanName);
            if (object == null) {
                // Make sure there is no cache before creating one
                FactoryBean#getObject FactoryBean#getObject FactoryBean#getObject FactoryBean#getObject FactoryBean#getObject
                object = doGetObjectFromFactoryBean(factory, beanName);
                // Only post-process and store if not put there already during getObject() call above
                // (e.g. because of circular reference processing triggered by custom getBean calls)
                Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                if(alreadyThere ! =null) {
                    // Retrieve the object from the cache again. If there is an object in the cache, override the current object
                    // The subBean's beanPostProcessor call process is skipped
                    // This is where the cyclic dependency problem is solved
                    // Students can think about, under what scenario, will you go to this branch?
                    object = alreadyThere;
                }
                else {
                    // The normal process is to go here, where we already have the subBean instance
                    if (shouldPostProcess) {
                        // If the subBean is already being created, return the subBean.
                        / / is judgment in singletonsCurrentlyInCreation the containers
                        if (isSingletonCurrentlyInCreation(beanName)) {
                            return object;
                        }
                        / / the current beanName join singletonsCurrentlyInCreation containers (set)
                        // If you do not add the container, you will report a loop dependency error
                        beforeSingletonCreation(beanName);
                        try {
                            // Call beanPostProcessor because of the subBean's initialization/destruction lifecycle
                            // Is managed by factoryBean itself, so this is what happens after the full instantiation of the bean is called
                            / / postProcessAfterInitialization method
                            // This is where the AOP aspect is done
                            object = postProcessObjectFromFactoryBean(object, beanName);
                        }
                        catch (Throwable ex) {
                            throw newBeanCreationException(...) ; }finally {
                            / / deleted from singletonsCurrentlyInCreation containerafterSingletonCreation(beanName); }}if (containsSingleton(beanName)) {
                        // Finally put into the cache
                        this.factoryBeanObjectCache.put(beanName, object); }}}returnobject; }}else {
        // Create a non-singleton
        Object object = doGetObjectFromFactoryBean(factory, beanName);
        if (shouldPostProcess) {
            try {
                / / call BeanPostProcessor
                object = postProcessObjectFromFactoryBean(object, beanName);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(...);
            }
        }
        / / return
        returnobject; }}Copy the code

We take a look at this simple postProcessObjectFromFactoryBean:

protected Object postProcessObjectFromFactoryBean(Object object, String beanName) {
    return applyBeanPostProcessorsAfterInitialization(object, beanName);
}

// This method is called after the bean is fully initialized in the bean initialization process
// Because subbeans leave the entire lifecycle (initialization, dependency injection) to factoryBean (user customization)
// So it just needs to call the buried point again
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
    throws BeansException {

    Object result = existingBean;
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        Object current = processor.postProcessAfterInitialization(result, beanName);
        if (current == null) {
            return result;
        }
        result = current;
    }
    return result;
}
Copy the code

As you can see, actually the final call to applyBeanPostProcessorsAfterInitialization method is bean initialization process, initializeBean method, bean fully initialized buried points method after the call. Aop also operates at this point, so our subbeans can also use AOP’s capabilities.

3. subBeanThe initialization time of

We’ve already seen the logic for processing factoryBean in getBean, which is simply a branching logic based on whether the name passed in is prefixed with & or not.

When is the singleton subBean object created and managed by Spring?

We know that calling getBean(“factoryBeanDemo”) directly will definitely create a subBean if there is no subBean for the factoryBeanObjectCache. Now what I want to say is, Our normal singleton beans are initialized when the Spring container starts. Are the singleton subbeans initialized at the same time? To understand this problem, we need to look directly at the source code. At this point, we need to look at the logic that initializes all the singleton beans when the Spring container starts:

If you are not familiar with the spring container startup process at the lifecycle level of beans, “A line by line Interpretation of Spring (4) – 10,000 words through the Bean Lifecycle (1) (2)”

/ / DefaultListableBeanFactory class
public void preInstantiateSingletons(a) throws BeansException {
	// Get all beanName
    List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

    for (String beanName : beanNames) {
        // Loop one by one
        RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
        if(! bd.isAbstract() && bd.isSingleton() && ! bd.isLazyInit()) {Singletons that are not abstract or lazily loaded need to be instantiated
            if (isFactoryBean(beanName)) {
                // beanDefinition is a factoryBean
                // If it is factoryBean, it will prefix beanName with an & and then call getBean
                // This getBean will not initialize the subBean instance
                Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
                if (bean instanceof FactoryBean) {
                    // After the bean instance is obtained, we can use instanceof to validate the bean instance
                    finalFactoryBean<? > factory = (FactoryBean<? >) bean;// You can see that there is a SmartFactoryBean interface with an isEagerInit method
                    // If the isEagerInit method returns true, spring considers the subBean to be pre-initialized
                    boolean isEagerInit = (factory instanceofSmartFactoryBean && ((SmartFactoryBean<? >) factory).isEagerInit());if (isEagerInit) {
                        // Call getBean again using the original beanName
                        // This triggers the subBean initialization processgetBean(beanName); }}}else {
                // Normal beans go directly heregetBean(beanName); }}}/ / to skip
}
Copy the code

As you can see, for our normal subBean, when the Spring container starts, it is not initializing, but only initializing the factoryBean object. Unless our factoryBean implements A subinterface of factoryBean, SmartFactoryBean, and indicates that the subBean needs to be initialized ahead of time.

Also take a quick look at the definition of the SmartFactoryBean interface:

public interface SmartFactoryBean<T> extends FactoryBean<T> {
    // similar to FactoryBean#isSingleton(), but with a slightly different use
	default boolean isPrototype(a) {
		return false;
	}
    // This method indicates whether to initialize ahead of time
	default boolean isEagerInit(a) {
		return false; }}Copy the code

4. subBeanCircular dependency problem

When we talk about circular dependency before, we are based on two ordinary beans to explain, and circular dependency phenomenon refers to spring in the singleton bean dependency injection, A->B, B->A problem.

You might say that the dependency injection of subbeans is not managed by Spring, so how can we have circular dependency problems?

If A->B and B->A appear, we will consider A and B instances to have circular dependencies. Spring, in the scope of its management, cleverly uses the level 3 cache / @lazy to solve the cyclic dependency.

Since factoryBean instances are themselves managed by the Spring container, it makes sense to do the following:

@Getter
@ToString
@AllArgsConstructor
public class SubBean {
    private A a;
}

@Component
public class A {
    @Autowired
    private SubBean subBean;
}

@Service
public class FactoryBeanDemo implements FactoryBean<SubBean>, BeanFactoryAware {
    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(final BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public SubBean getObject(a) throws Exception {
        final A bean = this.beanFactory.getBean(A.class);
        return newSubBean(bean); }}Copy the code

We have A beanFactory instance that factoryBean gets through the BeanFactoryAware interface, Beanfactory.getbean (a.lass) is used to retrieve an instance of a from the Spring container, which is dependent on the subBean instance…

Some of you might think that I’m struggling with Spring, why would I force such a complex structure to build a circular dependency?

But aren’t a instances and subbeans ultimately managed by Spring? Shouldn’t it solve that problem?

Of course it can be resolved, but this place should be discussed in two cases.

The following discussion involves the principle of spring three-level cyclic dependency. Students who are not clear can go to “Line by line Interpretation of Spring (5) – no one knows more about cyclic dependency than ME!” Learn about it.

3.1. Initialize firstaThe instance

In the case that a instance is initialized first, spring’s original three-level cache design can solve this problem. As you may recall, we exposed an instance of A to the cache after we created it, but before we did the dependency injection subBean. When we inject the subBean, we trigger the FactoryBean#getObject method, which eventually calls the logic of our own beanfactory.getbean (a.lass) to get the instance of a exposed to the cache from the cache.

So according to this process down, in fact, the overall is no problem, spring’s three-level cache design has been a good solution to this circular dependency problem.

Let’s take a quick look at the flow chart:

3.2. Initialize firstsubBeanThe instance

Normal subBean initialization is similar to lazy loading, that is, it does not trigger the initialization before a is initialized. But sometimes in our projects, instance dependencies may not be so clear.

Suppose we have an instance of C that relies on a subBean instance, which in turn relies on a subBean instance. If the c instance is initialized before the A instance, then the subBean instance is initialized before the A instance. Since our subBean has no multi-level caching mechanism to solve the cyclic dependency problem, the entire initialization process becomes:

As you can see, without special treatment, even though our ordinary bean has a three-level cache design, there is no completely unsolvable problem of cascading instances. However, it also causes our factoryBean#getObject to be called twice, generating two subBean objects, and the final subBean1 object in the factoryBeanObjectCache is not the same as the subBean2 object injected in the instance a.

So how can this situation be resolved? Some students may say, use multi-level cache, and ordinary beans and the same idea can be.

However, the idea of multi-level caching is to expose the bean instance to the cache after the bean instance is created but before dependency injection to solve the circular dependency problem. However, in the example we just gave, we actually injected the dependency when factoryBean#getObject gets the subBean instance (although we manually called beanfactory.getbean to get the dependency), which is a bit like constructor injection. The idea of multilevel caching for constructor cyclic dependencies doesn’t work either. So how does Spring solve the problem of two subBean instances? Spring solves this problem with just a few lines of code:

protected Object getObjectFromFactoryBean(FactoryBean<? > factory, String beanName,boolean shouldPostProcess) {
    // note that the isSingleton is FactoryBean#isSingleton
    FactoryBean is a singleton -containsSingleton(beanName),
    // The subBean is cached only when the subBean is defined as a singleton
    if (factory.isSingleton() && containsSingleton(beanName)) {
        synchronized (getSingletonMutex()) {
            / / lock
            // Take it from the cache first
            Object object = this.factoryBeanObjectCache.get(beanName);
            if (object == null) {
                // Make sure there is no cache before creating one
                FactoryBean#getObject FactoryBean#getObject FactoryBean#getObject FactoryBean#getObject FactoryBean#getObject
                object = doGetObjectFromFactoryBean(factory, beanName);
                Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                if(alreadyThere ! =null) {
                    // Retrieve the object from the cache again. If there is an object in the cache, override the current object
                    // The subBean's beanPostProcessor call process is skipped
                    // This is where the cyclic dependency problem is solved
                    object = alreadyThere;
                }
                else {
                    // Skip the logic of calling beanPostProcessor
                    this.factoryBeanObjectCache.put(beanName, object); }}returnobject; }}/ / to skip
}
Copy the code

FactoryBean# getObject retrieves subBean1 from factoryBeanObjectCache. If you get subBean2, you’re going to have a loop dependency like we did here. The subBean instance already exists in the cache. SubBean2 is assigned to object and returned. SubBean1 is discarded and not cached. This neatly solves the problem of two subbeans

3.3. Unresolvable circular dependency

As we discussed earlier, the use of bean factor #getBean for dependency injection in factoryBean#getObject is essentially constructor injection.

As we mentioned in the last article on loop caching, constructor loop dependencies are normally unresolvable:

@Getter
@ToString
@AllArgsConstructor
public class SubBean {
    private A a;
}

@Component
public class A {
    public A(final SubBean subBean) {
        this.subBean = subBean;
    }
    private SubBean subBean;
}

@Service
public class FactoryBeanDemo implements FactoryBean<SubBean>, BeanFactoryAware {
    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(final BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public SubBean getObject(a) throws Exception {
        final A bean = this.beanFactory.getBean(A.class);
        return newSubBean(bean); }}Copy the code

Let’s start:

public void test(a) {
    applicationContext = new AnnotationConfigApplicationContext("com.xiaoxizi.spring");
    Object subBean = applicationContext.getBean("factoryBeanDemo");
    System.out.println(subBean);
}
Copy the code

Error:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
Copy the code

Of course we can still use @lazy to solve this problem:

@Component
public class A {
    public A(@Lazy final SubBean subBean) {
        this.subBean = subBean;
    }
    private SubBean subBean;
}
Copy the code

In this case, Spring works fine because we’ve broken the chain of circular dependencies with @Lazy.

So here’s the really unsolvable circular dependency problem:

@AllArgsConstructor
public class SubBeanA {
    private SubBeanB b;
}

@AllArgsConstructor
public class SubBeanB {
    private SubBeanA a;
}

@Service
public class FactoryBeanA implements FactoryBean<SubBeanA>, BeanFactoryAware {
    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(final BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public SubBeanA getObject(a) throws Exception {
        final SubBeanB bean = (SubBeanB)this.beanFactory.getBean("factoryBeanB");
        return newSubBeanA(bean); }}@Service
public class FactoryBeanB implements FactoryBean<SubBeanB>, BeanFactoryAware {
    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(final BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public SubBeanB getObject(a) throws Exception {
        final SubBeanA bean = (SubBeanA)this.beanFactory.getBean("factoryBeanA");
        return newSubBeanB(bean); }}Copy the code

This kind of circumstance can direct the stack overflow, there will be no BeanCurrentlyInCreationException anomalies. The main reason is that the spring is in the call factoryBean# getObject then circular dependencies are detected using singletonsCurrentlyInCreation container, and this kind of circular dependencies, Factorybean #getObject -> factoryBean #getObject -> factoryBean #getObject -> factoryBean #getObject -> factoryBean #getObject -> factoryBean #getObject -> factoryBean #getObject , directly resulting in stack overflow.

protected Object getObjectFromFactoryBean(FactoryBean<? > factory, String beanName,boolean shouldPostProcess) {
    if (factory.isSingleton() && containsSingleton(beanName)) {
        synchronized (getSingletonMutex()) {
            // The stack overflows at this point
            Object object = this.factoryBeanObjectCache.get(beanName);
            if (object == null) {
                Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                if(alreadyThere ! =null) {
                    object = alreadyThere;
                }
                else {
                    // This is the normal way to go
                    if (shouldPostProcess) {
                        // This is where cyclic dependency detection is performed
                        if (isSingletonCurrentlyInCreation(beanName)) {
                            return object;
                        }
                        // This is where cyclic dependency detection is performed
                        beforeSingletonCreation(beanName);
                        try {
                            object = postProcessObjectFromFactoryBean(object, beanName);
                        }
                        catch (Throwable ex) {
                            throw newBeanCreationException(...) ; }finally{ afterSingletonCreation(beanName); }}if (containsSingleton(beanName)) {
                        this.factoryBeanObjectCache.put(beanName, object); }}}returnobject; }}/ / to skip
}
Copy the code

Don’t write code like this, you’ll be fired

Four, summary

This blog post focuses on the FactoryBean interface in Spring. This interface is actually spring’s support for the factory pattern.

By reading the source code, we know:

  1. The Singleton’s factoryBean object itself is actively initialized when the Spring container starts. The initialization of a subBean is triggered the first time it needs to be fetched.

  2. If the factoryBean object implements the Interface to SmartFactoryBean and the isEagerInit method returns true, then the subBean object will also be initiatively initialized when the Spring container starts.

  3. If the bean instance corresponding to beanName is a factoryBean when the bean is registered, then the object we get from getBean(beanName) will bea subBean object; To get the factory object factoryBean, you need to use getBean(“&” + beanName).

  4. Singleton subBean will be cached in the spring container, in particular container is FactoryBeanRegistrySupport# factoryBeanObjectCache, a Map < beanName, subBean instance >.

  5. Spring’s three-level cache design solves most of the circular dependency problems. However, the circular dependency on subbeans and ordinary beans can cause two subBean objects. Spring uses multiple checks to discard one of the subbeans that is useless. Keep the subBean instance that has been injected by another bean.

  6. The circular dependency between two different subbeans in the acquisition logic factoryBean#getObject cannot be resolved because the injection is similar to constructor injection for spring, that is, the circular dependency is a constructor circular dependency and cannot be broken by @lazy. So don’t write that code.

Creation is not easy, reprint please indicate the author: gold @ small xi son + source link ~

If you want to learn more about Spring source code, click to go to the rest of the line-by-line parsing Spring series

٩ (* ఠ O ఠ) = 3 ⁼ after ₌ ₃ ⁼ after ₌ ₃ ⁼ after ₌ ₃ du la la la la…

Here is the new blogger Shiko, all the big guys have seen this, click “like” on the upper left corner before you go ~~