Spring IOC container source code analysis - get singleton bean

public Object getBean(String name) throws BeansException {
    // getBean is an empty shell method. All logic is encapsulated in the doGetBean method
    return doGetBean(name, null, null, false);
}

protected <T> T doGetBean(
        final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
        throws BeansException {

    /*
     * Get the beanName through name. There are two reasons why you don't use name as beanName:
     * 1. name It may start with the & character to indicate that the caller wants to get FactoryBean itself, not FactoryBean 
     *    The bean created by the implementation class. In BeanFactory, the implementation class of FactoryBean and other bean stores
     *    The method is consistent, that is, there is no & character in < bean name, bean >, bean name. So we need
     *    Remove the first character & of name so that the FactoryBean instance can be retrieved from the cache.
     * 2. If name is an alias, the alias should be converted to a specific instance name, that is, bean name.
     */
    final String beanName = transformedBeanName(name);
    Object bean;

    /*
     * Get the singleton bean from the cache. Spring uses Map as the cache for bean names and bean instances, so this
     * For now, getSingleton(beanName) can be equivalent to beanMap.get(beanName). Of course, it's practical
     * Logic is not so simple. I'll talk about it later.
     */
    Object sharedInstance = getSingleton(beanName);

    /*
     * If sharedInstance = null, there is no corresponding instance in the cache, indicating that this instance has not been created.
     * BeanFactory Instead of instantiating all the singleton bean s at the beginning, we call getBean to get 
     * bean And then instantiate, that is, lazy loading.
     * getBean Methods have many overloads, such as getBean(String name, Object... args), which we get for the first time
     * When a bean is used, the parameter array (args) used to initialize the bean can be passed in, and BeanFactory will use these parameters
     * To match the appropriate constructor to construct the bean instance. Of course, if the singleton bean has already been created, there will be no args here
     * When used, BeanFactory does not instantiate a singleton bean multiple times.
     */
    if (sharedInstance != null && args == null) {
        if (logger.isDebugEnabled()) {
            if (isSingletonCurrentlyInCreation(beanName)) {
                logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                        "' that is not fully initialized yet - a consequence of a circular reference");
            }
            else {
                logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
            }
        }

        /*
         * If sharedInstance is a normal singleton bean, the following method will return it directly. But if 
         * sharedInstance If it is of FactoryBean type, you need to call getObject factory method to get the real 
         * bean Example. If you want to get the FactoryBean itself, you will not do any special processing here. You can directly return
         * Yes. After all, FactoryBean's implementation class itself is a bean, with only a little special function.
         */
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
    /*
     * If the above conditions are not met, the sharedInstance may be empty. At this time, the bean corresponding to beanName 
     * The instance may not have been created. There is another possibility. If the current container has a parent container, the bean instance corresponding to beanName
     * It may be created in the parent container, so you need to check the parent container before creating an instance.
     */
    else {
        // BeanFactory does not cache a bean of type Prototype and cannot handle the circular dependency of this type of bean
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }

        // If sharedInstance = null, find the bean instance in the parent container
        BeanFactory parentBeanFactory = getParentBeanFactory();
        if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
            // Get the beanName corresponding to name. If the name starts with the & character, return & + beanName
            String nameToLookup = originalBeanName(name);
            // Determine which method to call the parent container to get the bean based on whether args is empty
            if (args != null) {
                return (T) parentBeanFactory.getBean(nameToLookup, args);
            } 
            else {
                return parentBeanFactory.getBean(nameToLookup, requiredType);
            }
        }

        if (!typeCheckOnly) {
            markBeanAsCreated(beanName);
        }

        try {
            // Merge the parent BeanDefinition and the child BeanDefinition. This method will be analyzed separately later
            final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            checkMergedBeanDefinition(mbd, beanName, args);

            // Check whether there are dependsOn dependencies. If so, initialize the dependent bean s first
            String[] dependsOn = mbd.getDependsOn();
            if (dependsOn != null) {
                for (String dep : dependsOn) {
                    /*
                     * Detect whether there is A dependency on loop dependency, and throw an exception if there is one. For example, A relies on B,
                     * B They also rely on A. their configuration is as follows:
                     *   <bean id="beanA" class="BeanA" depends-on="beanB">
                     *   <bean id="beanB" class="BeanB" depends-on="beanA">
                     *   
                     * beanA It requires bean B to be created before it, but bean B requires bean a to precede it
                     * Establish. At this time, a loop is formed. For the dependencies on loop, Spring will directly
                     * Throw exception
                     */
                    if (isDependent(beanName, dep)) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                    }
                    // Register dependency records
                    registerDependentBean(dep, beanName);
                    try {
                        // Loading dependencies on dependencies
                        getBean(dep);
                    } 
                    catch (NoSuchBeanDefinitionException ex) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                    }
                }
            }

            // Create bean instance
            if (mbd.isSingleton()) {
                /*
                 * Instead of directly calling the createBean method to create a bean instance, you can create a bean instance through 
                 * getSingleton(String, ObjectFactory) Method to get the bean instance.
                 * getSingleton(String, ObjectFactory) Method will be called internally 
                 * ObjectFactory The getObject() method of creates the bean, and after the creation is completed,
                 * Put the bean in the cache. For the analysis of getSingleton method, this article will not expand first, I will
                 * Analysis in the following articles
                 */
                sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                    @Override
                    public Object getObject() throws BeansException {
                        try {
                            // Create bean instance
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            destroySingleton(beanName);
                            throw ex;
                        }
                    }
                });
                // If the bean is of type FactoryBean, the factory method is called to get the real bean instance. Otherwise, directly return the bean instance
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }

            // Create a bean instance of type prototype
            else if (mbd.isPrototype()) {
                Object prototypeInstance = null;
                try {
                    beforePrototypeCreation(beanName);
                    prototypeInstance = createBean(beanName, mbd, args);
                }
                finally {
                    afterPrototypeCreation(beanName);
                }
                bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
            }

            // Create other types of bean instances
            else {
                String scopeName = mbd.getScope();
                final Scope scope = this.scopes.get(scopeName);
                if (scope == null) {
                    throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                }
                try {
                    Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
                        @Override
                        public Object getObject() throws BeansException {
                            beforePrototypeCreation(beanName);
                            try {
                                return createBean(beanName, mbd, args);
                            }
                            finally {
                                afterPrototypeCreation(beanName);
                            }
                        }
                    });
                    bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                }
                catch (IllegalStateException ex) {
                    throw new BeanCreationException(beanName,
                            "Scope '" + scopeName + "' is not active for the current thread; consider " +
                            "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                            ex);
                }
            }
        }
        catch (BeansException ex) {
            cleanupAfterBeanCreationFailure(beanName);
            throw ex;
        }
    }

    // If a type conversion is required, do it here. I haven't looked into type conversion, so I won't talk about it.
    if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
        try {
            return getTypeConverter().convertIfNecessary(bean, requiredType);
        }
        catch (TypeMismatchException ex) {
            if (logger.isDebugEnabled()) {
                logger.debug("Failed to convert bean '" + name + "' to required type '" +
                        ClassUtils.getQualifiedName(requiredType) + "'", ex);
            }
            throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
        }
    }

    // Return to bean
    return (T) bean;
}

After reading the source code, I will briefly summarize the execution process of doGetBean. As follows:

Convert beanName
 Get instance from cache
 If the instance is not empty and args = null. Call getObjectForBeanInstance method and return the corresponding bean instance according to the name rule
 If the above condition is not true, search the bean instance of the beanName pair in the parent container, and if it exists, return it directly
 If the parent container does not exist, proceed to the next step -- merge BeanDefinition
 Handling dependencies on dependencies
 Create and cache bean s
 Call getObjectForBeanInstance method and return the corresponding bean instance according to the name rule
 Convert the bean type as needed and return the converted bean instance.

The flow chart corresponding to the above steps is as follows:

2.2 beanName conversion

The first thing spring does before getting the bean instance is to transform the parameter name. The purpose of the transformation is to solve two problems. The first is to process the name starting with the character & to prevent BeanFactory from finding the bean instance corresponding to the name. The second is to deal with the alias problem. Spring does not store the mapping of < alias, bean instance > but only < bean name, bean >. Therefore, to avoid that BeanFactory cannot find the bean instance corresponding to name, the alias should also be converted. Next, let's briefly analyze the transformation process as follows:

protected String transformedBeanName(String name) {
    // Two methods are called here: BeanFactoryUtils.transformedBeanName(name) and canonicalName
    return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}

/** This method is used to process & Characters */
public static String transformedBeanName(String name) {
    Assert.notNull(name, "'name' must not be null");
    String beanName = name;
    // Loop & character. For ex amp le, name = "& & & & & helloService" will eventually be converted to helloService
    while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
        beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
    }
    return beanName;
}

/** This method is used to convert aliases */
public String canonicalName(String name) {
    String canonicalName = name;
    String resolvedName;
    /*
     * The while loop is used here because there may be multiple aliases, that is, aliases point to aliases. For example, below
     * Configuration:
     *   <bean id="hello" class="service.Hello"/>
     *   <alias name="hello" alias="aliasA"/>
     *   <alias name="aliasA" alias="aliasB"/>
     *
     * The above alias pointing relationship is aliasb - > aliasa - > hello. For the above alias configuration, the data in the aliasMap
     * The view is: aliasmap = [< aliasb, aliasa >, < aliasa, hello >]. Resolve the alias through the following loop
     * aliasB The ultimate beanName
     */
    do {
        resolvedName = this.aliasMap.get(canonicalName);
        if (resolvedName != null) {
                canonicalName = resolvedName;
        }
    }
    while (resolvedName != null);
    return canonicalName;
}

2.3 get bean instance from cache

For singleton beans, the Spring container is instantiated only once. In the subsequent retrieval, you only need to retrieve it directly from the cache, and you don't need and can't instantiate it again (otherwise, a single instance is meaningless). The method to get the bean instance from the cache is getSingleton(String). Let's take a look at the implementation of this method. As follows:

public Object getSingleton(String beanName) {
    return getSingleton(beanName, true);
}

/**
 * Here we explain the allowerlyreference parameter. Allowerlyreference indicates whether other bean references are allowed
 * The bean in is being created to handle circular references. For circular references, let's give you a brief introduction. First look at the following configuration:
 *
 *   <bean id="hello" class="xyz.coolblog.service.Hello">
 *       <property name="world" ref="world"/>
 *   </bean>
 *   <bean id="world" class="xyz.coolblog.service.World">
 *       <property name="hello" ref="hello"/>
 *   </bean>
 * 
 * As shown above, hello depends on world, and world depends on Hello, forming a circular dependency between them. Spring is building 
 * hello When this bean is used, it will be detected that it depends on the world, so instantiate the world first. When instantiating world, it is found that 
 * world Depends on Hello. At this time, the container has to initialize hello. Because Hello is already in the initialization process, in order to 
 * world To finish the initialization, first let the world reference the Hello being initialized. After World initialization, hello 
 * You can reference to the world instance, so that hello can complete the initial. About circular dependence, I will write a special article later
 * That's a lot to say here.
 */
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // Get instances from singletonObjects. The cached instances in singletonObjects are fully instantiated bean s, which can be used directly
    Object singletonObject = this.singletonObjects.get(beanName);
    /*
     * If singletonObject = null, it indicates that it has not been created or completely created.
     * Here, judge whether bean corresponding to beanName is being created
     */
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // Get pre exposed bean s from earlySingletonObjects to handle circular references
            singletonObject = this.earlySingletonObjects.get(beanName);
            // If singletonObject = null and the bean instance is allowed to be exposed in advance, obtain a raw bean from the corresponding ObjectFactory (the property has not been filled)
            if (singletonObject == null && allowEarlyReference) {
                // Get the corresponding factory class
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // Expose bean instance in advance to solve circular dependency
                    singletonObject = singletonFactory.getObject();
                    // Put it into the cache. If there are other beans that depend on the current bean, other beans can get results directly from earlySingletonObjects
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return (singletonObject != NULL_OBJECT ? singletonObject : null);
}

Although the above code is not long, it involves several cache collections. If you don't know what the purpose of these caches is, the above source code may be difficult to understand. These cache collections are frequently used, and they will appear in later code, so I will introduce them here. As follows:

As far as getSingleton is concerned, there are not many source codes for getSingleton. But it involves the logic of circular dependency. If you don't understand this part, you may not know what the code is about. When I analyze the circular dependency later, I will analyze this method again, so it doesn't matter if I don't understand it for the time being.

2.4 merge parent and child beandefinitions

Spring supports configuration inheritance. In the tag, you can use the parent property to configure the parent bean. In this way, the subclass bean can inherit the configuration information of the parent bean, and also override the configuration in the parent class. For example, the following configuration:

<bean id="hello" class="xyz.coolblog.innerbean.Hello">
    <property name="content" value="hello"/>
</bean>

<bean id="hello-child" parent="hello">
    <property name="content" value="I`m hello-child"/>
</bean>

As shown above, the Hello child configuration inherits from hello. Hello child does not configure the class attribute. Here we let it inherit the class attribute in the parent configuration. Then we write some code to test it, as follows:

String configLocation = "application-parent-bean.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configLocation);
System.out.println("hello -> " + applicationContext.getBean("hello"));
System.out.println("hello-child -> " + applicationContext.getBean("hello-child"));

The test results are as follows:

From the test results, it can be seen that hello child was successfully instantiated under the unconfigured class attribute, indicating that it successfully inherited the class attribute of the parent configuration.
After watching the code demonstration, let's take a look at the source code. As follows:

protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
    // Check whether there is a "merged BeanDefinition" in the cache. If there is a direct return, you can
    RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
    if (mbd != null) {
        return mbd;
    }
    // Call overloaded method
    return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}

protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd)
        throws BeanDefinitionStoreException {
    // Continue to call overloaded methods
    return getMergedBeanDefinition(beanName, bd, null);
}

protected RootBeanDefinition getMergedBeanDefinition(
        String beanName, BeanDefinition bd, BeanDefinition containingBd)
        throws BeanDefinitionStoreException {

    synchronized (this.mergedBeanDefinitions) {
        RootBeanDefinition mbd = null;

        // I haven't got to know the purpose of containingBd in detail. Although we can know the general purpose of containingBd from the method annotation, we won't talk about it without detailed analysis. Forgive me
        if (containingBd == null) {
            mbd = this.mergedBeanDefinitions.get(beanName);
        }

        if (mbd == null) {
            // bd.getParentName() == null, indicating no parent configuration. In this case, upgrade the current BeanDefinition to RootBeanDefinition
            if (bd.getParentName() == null) {
                if (bd instanceof RootBeanDefinition) {
                    mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
                }
                else {
                    mbd = new RootBeanDefinition(bd);
                }
            }
            else {
                BeanDefinition pbd;
                try {
                    String parentBeanName = transformedBeanName(bd.getParentName());
                    /*
                     * Determine whether the name of the parent class beanName and the child class beanName are the same. If the same, the parent bean must
                     * In the parent container. The reason is very simple. The bottom layer of the container uses Map to cache < beanname, bean > key value pairs
                     * . Under the same container, it is obviously inappropriate to map two bean instances with the same bean name.
                     * Some friends may think that it can be stored as follows: < beanname, [bean1, bean2] > seems to be solved
                     * One to many questions. But there is also a problem, which bean is returned when getName(beanName) is called 
                     * How about an example?
                     */
                    if (!beanName.equals(parentBeanName)) {
                        /*
                         * Here we call getMergedBeanDefinition again, but the parameter value changes to 
                         * parentBeanName,For merging the parent BeanDefinition and grandparent 
                         * BeanDefinition. If Grandpa's bean definition still has a father 
                         * BeanDefinition,Then continue to merge
                         */
                        pbd = getMergedBeanDefinition(parentBeanName);
                    }
                    else {
                        // Get the parent container and judge the type of the parent container. If it is not ConfigurableBeanFactory, an exception will be thrown
                        BeanFactory parent = getParentBeanFactory();
                        if (parent instanceof ConfigurableBeanFactory) {
                            pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
                        }
                        else {
                            throw new NoSuchBeanDefinitionException(parentBeanName,
                                    "Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +
                                    "': cannot be resolved without an AbstractBeanFactory parent");
                        }
                    }
                }
                catch (NoSuchBeanDefinitionException ex) {
                    throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,
                            "Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);
                }
                // Create a RootBeanDefinition based on the configuration information of the parent BeanDefinition, that is, "merged BeanDefinition"
                mbd = new RootBeanDefinition(pbd);
                // Override the properties in the parent BeanDefinition with the properties in the child BeanDefinition
                mbd.overrideFrom(bd);
            }

            // If the scope property is not configured by the user, it is configured as singleton by default
            if (!StringUtils.hasLength(mbd.getScope())) {
                mbd.setScope(RootBeanDefinition.SCOPE_SINGLETON);
            }

            if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
                mbd.setScope(containingBd.getScope());
            }

            if (containingBd == null && isCacheBeanMetadata()) {
                // Cache merged BeanDefinition
                this.mergedBeanDefinitions.put(beanName, mbd);
            }
        }

        return mbd;
    }
}

Although the source code above is a little long, the good thing is that the logic is not very complex. In addition, I annotated the source code in detail. I think I can read it patiently. I won't talk about it here.

2.5 get bean instance from FactoryBean

After so many steps, we are almost at the end of the doGetBean method. In this section, let's look at the process of getting a bean instance from the FactoryBean implementation class. As for the usage of FactoryBean, I have demonstrated it in the introduction article, so I will not explain it again. Next, let's go straight to the topic. The relevant source code is as follows:

protected Object getObjectForBeanInstance(
        Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {

    // If the name starts with & but the beanInstance is not a FactoryBean, it is considered to be a problem.
    if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
        throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
    }

    /* 
     * If the above judgment is passed, it indicates that beanInstance may be a normal bean or a bean 
     * FactoryBean. If it is a normal bean, you can directly return beanInstance here. If it is 
     * FactoryBean,The factory method is called to generate a bean instance.
     */
    if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
        return beanInstance;
    }

    Object object = null;
    if (mbd == null) {
        /*
         * If the mbd is empty, the bean is loaded from the cache. The singleton bean generated by FactoryBean will be cached
         * In the factoryBeanObjectCache collection, you don't have to create it every time
         */
        object = getCachedObjectForFactoryBean(beanName);
    }
    if (object == null) {
        // Based on the previous judgment, it can be ensured that beanInstance is of FactoryBean type, so type conversion can be performed
        FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
        // If the mbd is empty, judge whether there is a BeanDefinition with the name beanName
        if (mbd == null && containsBeanDefinition(beanName)) {
            // Merge BeanDefinition
            mbd = getMergedLocalBeanDefinition(beanName);
        }
        // Synthetic literally means "synthetic.". Through global lookup, I found that this attribute would be set to true in AOP related classes.
        // So I think this field may indicate whether a bean has been enhanced by AOP, that is, AOP has synthesized a new proxy class based on the original class.
        // But at present, it's just speculation, not deep research. If you have any friends who know the specific meaning of this field, please do not hesitate to comment
        boolean synthetic = (mbd != null && mbd.isSynthetic());

        // Call getObjectFromFactoryBean method to continue to get the instance
        object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    }
    return object;
}

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
    /*
     * FactoryBean There are also single examples and non single examples. For different types of factorybeans, there are two processing methods:
     *   1. bean instances generated by a singleton FactoryBean are also considered singleton types. Need to be put into cache for future reuse
     *   2. The bean instance generated by non singleton FactoryBean will not be put into the cache, and a new instance will be created every time
     */
    if (factory.isSingleton() && containsSingleton(beanName)) {
        synchronized (getSingletonMutex()) {
            // Take bean instance from cache to avoid creating bean instance multiple times
            Object object = this.factoryBeanObjectCache.get(beanName);
            if (object == null) {
                // Create an instance from a factory object
                object = doGetObjectFromFactoryBean(factory, beanName);
                Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                if (alreadyThere != null) {
                    object = alreadyThere;
                }
                else {
                    // shouldPostProcess is equivalent to! Synchronous in the previous method, which is used to indicate whether postprocessing is applied
                    if (object != null && shouldPostProcess) {
                        if (isSingletonCurrentlyInCreation(beanName)) {
                            return object;
                        }
                        beforeSingletonCreation(beanName);
                        try {
                            // Apply postprocessing
                            object = postProcessObjectFromFactoryBean(object, beanName);
                        }
                        catch (Throwable ex) {
                            throw new BeanCreationException(beanName,
                                    "Post-processing of FactoryBean's singleton object failed", ex);
                        }
                        finally {
                            afterSingletonCreation(beanName);
                        }
                    }
                    // The beanName here corresponds to the implementation class of FactoryBean. The implementation class of FactoryBean will also be instantiated and cached in singletonObjects
                    if (containsSingleton(beanName)) {
                        // Instances created by FactoryBean will be cached in FactoryBean objectcache for subsequent calls
                        this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));
                    }
                }
            }
            return (object != NULL_OBJECT ? object : null);
        }
    }
    // Get non singleton instance
    else {
        // Get instance from factory class
        Object object = doGetObjectFromFactoryBean(factory, beanName);
        if (object != null && shouldPostProcess) {
            try {
                // Apply postprocessing
                object = postProcessObjectFromFactoryBean(object, beanName);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
            }
        }
        return object;
    }
}

private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
        throws BeanCreationException {

    Object object;
    try {
        // The logic of if branch is Java security code, which can be ignored. Look directly at the code of else branch
        if (System.getSecurityManager() != null) {
            AccessControlContext acc = getAccessControlContext();
            try {
                object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
                    @Override
                    public Object run() throws Exception {
                            return factory.getObject();
                        }
                    }, acc);
            }
            catch (PrivilegedActionException pae) {
                throw pae.getException();
            }
        }
        else {
            // Call factory method to generate bean instance
            object = factory.getObject();
        }
    }
    catch (FactoryBeanNotInitializedException ex) {
        throw new BeanCurrentlyInCreationException(beanName, ex.toString());
    }
    catch (Throwable ex) {
        throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
    }

    if (object == null && isSingletonCurrentlyInCreation(beanName)) {
        throw new BeanCurrentlyInCreationException(
                beanName, "FactoryBean which is currently in creation returned null from getObject");
    }
    return object;
}

The above source code analysis is finished. Although the code is long, the overall logic is not very complex. Here is a brief summary. getObjectForBeanInstance and the methods it calls mainly do the following:

Check the type of parameter beanInstance. If it is a bean of non FactoryBean type, directly return
 Check whether the FactoryBean implementation class is of singleton type, and conduct different processing for singleton and non singleton types
 For a single FactoryBean, first obtain the instance generated by FactoryBean from the cache
 If the cache misses, call the FactoryBean.getObject() method to generate an instance and put it into the cache
 For non singleton factorybeans, each time a new instance is created directly, there is no need to cache
 If shouldPostProcess = true, whether it is a single instance or an instance generated by a non single FactoryBean, post processing is required

This section deals with two features of FactoryBean and postprocessing. For unfamiliar students, please refer to my instructions in the introduction. There is no more explanation here.

3. summary

At this point, the content of getting bean instance by Spring IOC container is analyzed. If you are reading Spring's source code for the first time, it doesn't matter if you don't understand it. Read it a few times, think about certification, I believe it can be understood. In addition, due to my limited level, the above source code analysis is wrong, but also hope to give more advice, thanks.

135 original articles published, 84 praised, 540000 visitors+
His message board follow

Tags: Spring Attribute shell xml

Posted on Thu, 12 Mar 2020 21:36:52 -0700 by loureiro