[Spring Source] IOC Implementation-bean Loading

Articles Catalogue

1. Transform the corresponding beanName

Why do you need to convert the incoming parameter name into the corresponding bean Name?
The parameter name passed in is bean Name, which may be an alias, or FactoryBean.

How to convert?

  1. Remove the modifiers of FactoryBean, such as name="&aa"-> name="aa"
  2. Remove the final bean Name represented by the specified alias, such as A's alias pointing to B and B to C, and return C.

2. Getting Singleton Beans in Cache

What's the meaning of this?
Singletons are created only once in the same container of spring, and subsequently retrieved bean s are retrieved directly from the singleton cache. Here is an attempt to load, first from the cache, and then again from the singleton Factories if the load is unsuccessful.

Why did you do that?
Dependency injection occurs when creating singleton beans, and to avoid cyclic dependencies when creating dependencies, the principle of creating beans in spring is to add the ObjectFactory that created beans to the cache ahead of time until the beans are created, and directly when the next beans are created that need to rely on the previous beans. Using ObjectFactory

Source Code Implementation

Process summary

  1. Try to get an instance from singleton Objects
  2. If it can't be retrieved, it can be retrieved from earlySingleton-Objects
  3. If not, try again to get the ObjectFactory corresponding to the bean Name from singleton Factories
  4. Call the getObject of the ObjectFactory to create the bean
  5. Put it in earlySingleton-Objects
  6. remove the ObjectFactory from singleton Facotories
@Override
@Nullable
public Object getSingleton(String beanName) {
    //The parameter true allows early dependencies
    return getSingleton(beanName, true);
}
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    //Check for instances in the cache
    Object singletonObject = this.singletonObjects.get(beanName);
    //Lock global variables if the cache is empty and the singleton bean is being created
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            //If the bean is loading, it will not be processed
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                //When some methods need to be initialized in advance, addSingletonFactory is directly called to store the corresponding ObjectFactory initialization strategy in singletonFactory.
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    //Call the preset getObject method
                    singletonObject = singletonFactory.getObject();
                    //Recorded in the cache, note that earlySingleton Objects and singleton Factories are mutually exclusive
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

3. Obtaining objects from instances of bean s

Once we get an instance of the bean, we need to call the getObjectForBeanInstance method to check for correctness (to check whether the current bean is a FactoryBean type bean) and, if so, to call getObject() in the corresponding FactoryBean instance of the bean as the return value.
The process summary of getObjectForBeanInstance method:

  1. Verification of FactoryBean Correctness
  2. Do nothing with non-FactoryBean s
  3. Converting bean s (converting GernericBean Definition, which stores XML configuration files, to RootBean Definition)
  4. Delegate the work of parsing bean s from Factory to getObjectFromFactoryBean

Code

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

	// Validation fails if the specified name is factory-related (prefixed with &) and the bean Instance is not a FactoryBean type
	if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
		throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
	}

	// Now we have an instance of a bean, which might be a normal bean or a FactoryBean.
	// If it's FactoryBean, we use it to create instances, but if the user wants to get the factory instance directly instead of the corresponding instance of the factory's getObject
	// Then the incoming name should be prefixed&
	if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
		return beanInstance;
	}

	// Loading FactoryBean
	Object object = null;
	if (mbd == null) {
		//Loading Bean s from Cache
		object = getCachedObjectForFactoryBean(beanName);
	}
	//Let the Bean factory produce a Bean object instance with a given name
	if (object == null) {
		// Now it's clear that the bean Instance must be a FactoryBean type
		FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
		//Detect whether beanName is defined in all loaded classes
		if (mbd == null && containsBeanDefinition(beanName)) {
			//Converting GenericBean Definition, which stores XML configuration files, to RootBean Definition
			//Get the Bean definition of the specified name from the container, and merge the base class related properties if the base class is inherited
			mbd = getMergedLocalBeanDefinition(beanName);
		}
		//Is it user-defined rather than application-defined?
		boolean synthetic = (mbd != null && mbd.isSynthetic());
		object = getObjectFromFactoryBean(factory, beanName, !synthetic);
	}
	return object;
}

4. Acquisition of singletons

process

(1) Check if the cache has been loaded
(2) If there is no loading, record the beanName's loading status
(3) Record the loading status before loading the singleton.

You may think that the beforeSingleton Creation method is an empty implementation with no logic in it, but there is a very important operation in this function: record the loading state, that is, record the beans currently being created in the cache through this. singletons CurrentlyInCreation. add (beanName), so that you can Detection of cyclic dependencies.

(4) Instantiate bean s by calling the individual Object method of the ObjectFactory passed in by parameters
(5) Processing method calls after loading a singleton

The record loading state of Synchronization Step 3 is similar. When the bean is loaded, it needs to remove the record of the bean's loading state from the cache.

(6) Record the results to the cache and delete the various auxiliary states recorded during loading bean s
(7) Return processing results

Source code

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(beanName, "Bean name must not be null");
    // Global variables need synchronization
    synchronized (this.singletonObjects) {
        // First check whether the corresponding beans have been loaded, because the singleton pattern is actually reusing the beans that have been created.
        Object singletonObject = this.singletonObjects.get(beanName);
        // If it's empty, you can initialize singleton bean s
        if (singletonObject == null) {
            if (this.singletonsCurrentlyInDestruction) {
                throw new BeanCreationNotAllowedException(beanName,
                        "Singleton bean creation not allowed while singletons of this factory are in destruction " +
                        "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
            }
            beforeSingletonCreation(beanName);
            boolean newSingleton = false;
            boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
            if (recordSuppressedExceptions) {
                this.suppressedExceptions = new LinkedHashSet<>();
            }
            try {
                // Initialize bean s
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            }
            catch (IllegalStateException ex) {
                // Has the singleton object implicitly appeared in the meantime ->
                // if yes, proceed with it since the exception indicates that state.
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    throw ex;
                }
            }
            catch (BeanCreationException ex) {
                if (recordSuppressedExceptions) {
                    for (Exception suppressedException : this.suppressedExceptions) {
                        ex.addRelatedCause(suppressedException);
                    }
                }
                throw ex;
            }
            finally {
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = null;
                }
                afterSingletonCreation(beanName);
            }
            if (newSingleton) {
                // Add Cache
                addSingleton(beanName, singletonObject);
            }
        }
        return singletonObject;
    }
}

5. Preparing to create bean s

process
(1) Setting class attribute or parsing class according to className
(2) Verify the MethodOverride attribute for validation and tagging

There is one method in the above code: mbd.prepareMethodOverrides();
In fact, there is no override-method configuration in Spring, but as we said before, lookup-method and replace-method exist in Spring configuration, and the loading of these two configurations is to store the configuration in the method Overrides property of BeanDefinition, and the operation of this function is that For these two configurations.

(3) Apply post-processor before initialization to analyze whether the specified bean has short-circuit operation before application initialization.
(4) Create bean s

Code

@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
    if (logger.isDebugEnabled()) {
        logger.debug("Creating instance of bean '" + beanName + "'");
    }
    RootBeanDefinition mbdToUse = mbd;
    
    // Lock class, parse class according to the set class attribute or according to className
    Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
    if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
        mbdToUse = new RootBeanDefinition(mbd);
        mbdToUse.setBeanClass(resolvedClass);
    }
    // Approaches to validation and preparation of coverage
    try {
        // Handling override attributes
        mbdToUse.prepareMethodOverrides();
    }
    catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),beanName, "Validation of method overrides failed", ex);}
    try {
        // Give BeanPostProcessors an opportunity to return the proxy instead of the real instance (instantiated preprocessing)
        Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
        if (bean != null) {
            return bean;
        }
    }
    catch (Throwable ex) { throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,"BeanPostProcessor before instantiation of bean failed", ex);}
    try {
        Object beanInstance = doCreateBean(beanName, mbdToUse, args);
        if (logger.isDebugEnabled()) {
            logger.debug("Finished creating instance of bean '" + beanName + "'");
        }
        return beanInstance;
    }
    catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { throw ex; }
    catch (Throwable ex) {throw new BeanCreationException( mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);}
}

6. Cyclic dependence

Loop Dependent vs Loop Call

Circular dependency: A refers to B, B to C, C to A (circular reference)
Loop calls: Loop calls between methods cannot be resolved, unless there is an end condition, otherwise it is a dead loop, which eventually leads to memory overflow.

How Spring Solves Cyclic Dependence

1. Constructor

Can not solve, can only throw exceptions (BeanCurrentlyInCreationException)

For example, when creating a TestA class, the constructor needs a TestB class, which will create TestB, and when creating a TestB class, it will find that the constructor needs a TestB class.
If you need a TestC class, you create a TestC. Finally, when you create TestC, you find that you need TestA to form a ring.
No way to create it.
The spring container places each bean identifier being created in a pool of currently created beans, and the bean identifier
Characters will remain in this pool throughout the creation process, so if you find yourself in the "current" during the creation of bean s
When creating a bean pool, the BeanCurrentlyInCreationException exception is thrown to indicate a cyclic dependency; for the created bean, it is removed from the Currently Created Bean Pool.

test

<bean id="testA" class="com.bean.TestA">
	<constructor-arg index="0" ref="testB"/> 
</bean>
<bean id="testB" class="com.bean.TestB">
	<constructor-arg index="0" ref="testC"/> 
</bean>
<bean id="testC" class="com.bean.TestC">
	<constructor-arg index="0" ref="testA"/> 
</bean>

process
1. Spring container creates "testA" beans. First, go to "Current Creation Bean Pool" to find out if the current bean is being created. If not, continue to prepare the constructor parameter testB it needs, and put the "testA" identifier into "Current Creation Bean Pool".
2. Spring container creates "testB" beans. First, go to "Current Creation bean Pool" to find out whether the current beans are being created. If not, continue to prepare the constructor parameter "testc" needed by Spring container, and put the "testB" identifier into "Current Creation bean Pool".
3. Spring container creates "testc" beans. First, go to "Current Creation Bean Pool" to find out if the current bean is being created. If not, continue to prepare the constructor parameter testA it needs, and put the "testC" identifier into "Current Creation Bean Pool".
4. So far, the Spring container is going to create a "testA" bean and finds that the bean identifier is in the "currently created bean"
In the pool, because it represents a cyclic dependency, a BeanCurrentlyInCreationException is thrown.

2,setter

what: Pre-expose bean s that have just completed constructor injection but have not completed other steps, such as setter injection, through the Spring container

how: Solves the cyclic dependencies of beans in a singleton scope. By exposing a single factory method ahead of time, other beans can refer to the bean.

Specific steps:
1. The Spring container creates a single "testA" bean. First, it creates a bean based on a parametric constructor and exposes an "ObjectFactory" to return a bean that exposes a creation ahead of time. Then, it places the "testA" identifier in the "Currently Created Bean Pool", and then proceeds to setter injection"testB.
2. The Spring container creates a single "testB" bean. First, it creates a bean based on a parametric constructor and exposes an "ObjectFactory" to return an object that exposes a created bean in advance. Then it places the "testB" identifier in the "Current Created Bean Pool" and performs setter injection "circle".
3. The Spring container creates a single "testC" bean. First, it creates a bean according to the parameter-free constructor and exposes an "ObjectFactory" to return a bean that exposes a creation ahead of time. Then it places the "testc" identifier in the "currently created bean pool" and injects "testA" into the setter. When injecting "testA", the "ObjectFactory" factory is exposed ahead of time, so that it can be used to return to expose a bean created ahead of time.
4. Finally, the setter injection is completed in dependency injection "testB" and "testA".

3. Dependency Processing of prototype Scope

For "prototype" scoped beans, the Spring container cannot complete dependency injection, because the Spring container does not cache "prototype" scoped beans, so it cannot expose a created bean in advance.
For "singleton" scoped bean s, circular references can be disabled by "setAIIowCircuIarReferences (false);".

7. Create bean s

7.1 Instances of creating bean s (instantiation)

Instance logic

  1. Generate instances of bean s based on configuration in RootBean Definition
  2. Analytic constructor
  3. Instance of constructors

Spring uses a caching mechanism to determine which constructor will eventually be used for instantiation based on parameters and types, but the judgment process consumes performance.

Two cases of instantiation:
1. General instantiate Bean
Direct invocation of instantiation policies
2. Autowire Constructor
2.1 Determination of Constructor Parameters
2.1.1 Judgement based on parameter explicitArgs (not empty, that is, the parameter of the constructor)
Getting in 2.1.2 cache (parameters in the cache may be the initial type or the final type, so they need to be filtered by type converter)
2.1.3 Configuration File Acquisition (information saved in BeanDefinition)
2.2 Confirmation of constructor (constructor, parameter name, parameter type, parameter value)
(Method of matching corresponding constructors: number of parameters)
2.2.1 Sort by the number of parameters of public and non-public constructors
2.2.2 Get the parameter name: Annotation, Tool Class ParameterNameDiscoverer
2.3 Convert the corresponding parameter type according to the determined constructor (type converter: Spring provided, user-defined)
2.4 Verification of Constructor Uncertainty (Parent-Child Relation)
2.5 Constructor+Constructor Parameters Based on Instance Policy --> Instance Bean

2.1, 2.2 are the basic conditions, and 2.5 is the combination.

Instance strategy:
1 - Users do not use methods that require dynamic changes (replace, lookup), but use reflection.
2-Otherwise, the interception enhancer Club containing the logic corresponding to the two features is set in the dynamic proxy mode.

7.2 Record ObjectFactory for creating bean s (handling cyclic dependencies)


Solutions to cyclic dependency:
When creating dependency A in B, the filling of attributes in A is interrupted by the instantiation method provided by ObjectFactory, so that A held in B is only A that has just initialized and not filled any attributes. This step of initializing A is done at the beginning of creating A, because A in A and B represents it. Attribute addresses are the same, so the attribute filling created in A can naturally be obtained through A in B.

7.3 Attribute Injection (Filling)

Processing flow

  1. Whether the postProcessAfterInstantiation control program continues to fill
  2. Dependent bean s are extracted according to injection types (byName, byType) and stored uniformly in Property Values
  3. Before the attribute is filled in, the attribute is processed again (eg: validation of the attribute)
  4. Fill all properties in Property Values into BeanWrapper

Get the injection attribute:

(1)autowireByName
Find the loaded bean s in the parameter Mutable Property Values passed in, instantiate them recursively, and then add them to Mutable Property Values

(2)autowireByType

  1. Try parsing with a parser
  2. For collection types, which are not within the scope of parsing, different cases of different collection types need to be dealt with again.

Applied to bean s that have been instantiated:

applyPropertyValues

  1. Get all attributes
  2. Get the corresponding parser
  3. Traversing attributes, converting attributes to the type of corresponding attributes of corresponding classes

7.4 Initialization bean s (invocation of user-defined initialization methods)

  1. Activate the Aware method (after the bean s that implement the Aware interface are initialized, the corresponding resources < BeanFactory instance, ApplicationContext instance >can be obtained)
  2. Processor applications (BeanPost Processor, giving users sufficient privileges to change/extend Spring)
  3. Activate custom init methods

7.5 Register Disposable Bean (Destruction Method)

Extended entrance to destruction methods
Destruction can also be done by configuring the attribute destroy-method method

Keywords: Spring Attribute xml

Added by Ryaan on Thu, 29 Aug 2019 15:00:22 +0300