Spring principle and source code analysis series - Spring IoC source code analysis

Posted by ahundiak on Thu, 10 Mar 2022 04:52:11 +0100

preface

IoC container is mainly divided into two stages: IoC container startup and dependency injection.
In the previous section
Spring principles and source code analysis series (4) - Spring IoC source code analysis (Part 1) we have analyzed the startup process of IoC container from the perspective of source code. This article will talk about the source code analysis of dependency injection process.

2, Dependency injection of IoC container

After the initialization of IoC container is completed, BeanDefinition data mapping has been established in the container.
Next, we need to start dependency injection.

First, trigger the dependency injection process through getBean(), which is mainly divided into two steps:

1. Instantiate the Bean: createBeanInstance();
2. Dependency injection of object relationship: populate().
Let's take a look at the specific implementation process.
First, the user calls the getBean() method, which is the entry of dependency injection and will trigger the dependency injection process.

AbstractBeanFactory.java

Override
    public Object getBean(String name) throws BeansException {
        return doGetBean(name, null, null, false);
    }

    @Override
    public <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException {
        return doGetBean(name, requiredType, null, false);
    }

    @Override
    public Object getBean(String name, Object... args) throws BeansException {
        return doGetBean(name, null, args, false);
    }

    public <T> T getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args)
            throws BeansException {

        return doGetBean(name, requiredType, args, false);
    }

 

The place where the Bean is actually obtained is also the place where dependency injection occurs:

AbstractBeanFactory.java

    //The place where the Bean is actually obtained and the place where the dependency injection is triggered
    protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
            @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

        final String beanName = transformedBeanName(name);
        Object bean;

        // Get the Bean from the cache first. If it is a singleton, you do not need to create it again
        Object sharedInstance = getSingleton(beanName);
        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 + "'");
                }
            }
            //Complete the relevant processing of FactoryBean
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        }

        else {
            // Fail if we're already creating this bean instance:
            // We're assumably within a circular reference.
            if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }

            //Check whether the BeanDefinition corresponding to beanName in IoC container exists. If the current container does not exist,
            // Then go to the parent BeanFactory (parent BeanFactory) to find it;
            //If the current parent BeanFactory does not exist, look up the parent BeanFactory chain
            // Check if bean definition exists in this factory.
            BeanFactory parentBeanFactory = getParentBeanFactory();
            if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                // Not found -> check parent.
                String nameToLookup = originalBeanName(name);
                if (parentBeanFactory instanceof AbstractBeanFactory) {
                    return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                            nameToLookup, requiredType, args, typeCheckOnly);
                }
                else if (args != null) {
                    // Find in parent BeanFactory
                    return (T) parentBeanFactory.getBean(nameToLookup, args);
                }
                else {
                    // No args -> delegate to standard getBean method.
                    return parentBeanFactory.getBean(nameToLookup, requiredType);
                }
            }

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

            try {
                //Obtain BeanDefinition according to beanName
                final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                checkMergedBeanDefinition(mbd, beanName, args);

                // Get all dependent beans of the current Bean. Trigger recursive call of getBean
                String[] dependsOn = mbd.getDependsOn();
                if (dependsOn != null) {
                    for (String dep : dependsOn) {
                        if (isDependent(beanName, dep)) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                        }
                        registerDependentBean(dep, beanName);
                        getBean(dep);
                    }
                }

                // In getSingleton, call createBean () in ObjectFactory to create an instance of Singleton Bean
                if (mbd.isSingleton()) {
                    sharedInstance = getSingleton(beanName, () -> {
                        try {
                            //Trigger the creation of Bean
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            // Explicitly remove instance from singleton cache: It might have been put there
                            // eagerly by the creation process, to allow for circular reference resolution.
                            // Also remove any beans that received a temporary reference to the bean.
                            destroySingleton(beanName);
                            throw ex;
                        }
                    });
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }
                //Create an instance of Prototype Bean
                else if (mbd.isPrototype()) {
                    // It's a prototype -> create a new instance.
                    Object prototypeInstance = null;
                    try {
                        beforePrototypeCreation(beanName);
                        //Trigger the creation of Bean
                        prototypeInstance = createBean(beanName, mbd, args);
                    }
                    finally {
                        afterPrototypeCreation(beanName);
                    }
                    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                }
                //Gets the Bean of the specified Scope type
                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, () -> {
                            beforePrototypeCreation(beanName);
                            try {
                                //Trigger the creation of Bean
                                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;
            }
        }

        // Type check the created Bean. If there is no problem, the newly created Bean is returned. This Bean already contains dependencies
        if (requiredType != null && !requiredType.isInstance(bean)) {
            try {
                T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
                if (convertedBean == null) {
                    throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
                }
                return convertedBean;
            }
            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 (T) bean;
    }

 

You can see that after the user calls the getBean() method, the doGetBean() method will be called, and then the createBean() method will be called to create the Bean and the dependency injection process.
Next, let's take a look at how beans are created and dependency injection of object relationships.

1. Instantiate the Bean

When the createBean() method is called, the
AbstractAutowireCapableBeanFactory.

AbstractAutowireCapableBeanFactory.java

    @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;

        // Judge whether the Bean to be created can be instantiated (the interface cannot be instantiated) and whether the class can be loaded by the class loader
        Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
        if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
            mbdToUse = new RootBeanDefinition(mbd);
            mbdToUse.setBeanClass(resolvedClass);
        }

        // Prepare method overrides.
        try {
            mbdToUse.prepareMethodOverrides();
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
                    beanName, "Validation of method overrides failed", ex);
        }

        try {
            // If the Bean is configured with BeanPostProcessors, a proxy is returned
            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 {
            //This is where the call actually creates the Bean
            Object beanInstance = doCreateBean(beanName, mbdToUse, args);
            if (logger.isDebugEnabled()) {
                logger.debug("Finished creating instance of bean '" + beanName + "'");
            }
            return beanInstance;
        }
        catch (BeanCreationException ex) {
            // A previously detected exception with proper bean creation context already...
            throw ex;
        }
        catch (ImplicitlyAppearedSingletonException ex) {
            // An IllegalStateException to be communicated up to DefaultSingletonBeanRegistry...
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
        }
    }

 

In createBean (), call doCreateBean () to create Bean specifically.

Go to doCreateBean() to see where the Bean is actually created:
AbstractAutowireCapableBeanFactory.java

    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {

        // Bean instantiation
        //BeanWrapper is used to hold the created Bean object
        BeanWrapper instanceWrapper = null;
        if (mbd.isSingleton()) {
            //If it is a Singleton, clear the Bean with the same name in the cache first
            instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
        }
        if (instanceWrapper == null) {
            //Here is where the Bean is created: createBeanInstance()
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        final Object bean = instanceWrapper.getWrappedInstance();
        Class<?> beanType = instanceWrapper.getWrappedClass();
        if (beanType != NullBean.class) {
            mbd.resolvedTargetType = beanType;
        }

        // Allow post-processors to modify the merged bean definition.
        synchronized (mbd.postProcessingLock) {
            if (!mbd.postProcessed) {
                try {
                    applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
                }
                catch (Throwable ex) {
                    throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                            "Post-processing of merged bean definition failed", ex);
                }
                mbd.postProcessed = true;
            }
        }

        // Eagerly cache singletons to be able to resolve circular references
        // even when triggered by lifecycle interfaces like BeanFactoryAware.
        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            if (logger.isDebugEnabled()) {
                logger.debug("Eagerly caching bean '" + beanName +
                        "' to allow for resolving potential circular references");
            }
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }

        // For Bean initialization, dependency injection generally occurs here.
        Object exposedObject = bean;
        try {
            //populateBean: Handling dependencies between objects
            populateBean(beanName, mbd, instanceWrapper);
            //initializeBean: initialization of a Bean
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
        catch (Throwable ex) {
            if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
                throw (BeanCreationException) ex;
            }
            else {
                throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
            }
        }

        if (earlySingletonExposure) {
            Object earlySingletonReference = getSingleton(beanName, false);
            if (earlySingletonReference != null) {
                if (exposedObject == bean) {
                    exposedObject = earlySingletonReference;
                }
                else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                    String[] dependentBeans = getDependentBeans(beanName);
                    Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
                    for (String dependentBean : dependentBeans) {
                        if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                            actualDependentBeans.add(dependentBean);
                        }
                    }
                    if (!actualDependentBeans.isEmpty()) {
                        throw new BeanCurrentlyInCreationException(beanName,
                                "Bean with name '" + beanName + "' has been injected into other beans [" +
                                StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                                "] in its raw version as part of a circular reference, but has eventually been " +
                                "wrapped. This means that said other beans do not use the final version of the " +
                                "bean. This is often the result of over-eager type matching - consider using " +
                                "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
                    }
                }
            }
        }

        // Register bean as disposable.
        try {
            registerDisposableBeanIfNecessary(beanName, bean, mbd);
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
        }

        return exposedObject;
    }

 

In this method, there are two important method calls that need attention:

createBeanInstance(): where an object contained in a Bean is instantiated, a BeanWrapper is returned (the reason why the returned BeanWrapper is not a Bean is that BeanWrapper facilitates subsequent value setting operations on beans);
populateBean(): after the Bean is created, the dependencies between objects need to be processed.


AbstractAutowireCapableBeanFactory.java

//  There are three methods to generate Java objects contained in beans: factory method, constructor function instantiation and default constructor instantiation (SimpleInstantiationStrategy provides reflection and CGLIB)
    protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
        // See whether the Bean can be instantiated (the interface cannot be instantiated)
        Class<?> beanClass = resolveBeanClass(mbd, beanName);

        if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                    "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
        }

        Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
        if (instanceSupplier != null) {
            return obtainFromSupplier(instanceSupplier, beanName);
        }

        if (mbd.getFactoryMethodName() != null)  {
            //Instantiate using factory methods
            return instantiateUsingFactoryMethod(beanName, mbd, args);
        }

        // Shortcut when re-creating the same bean...
        boolean resolved = false;
        boolean autowireNecessary = false;
        if (args == null) {
            synchronized (mbd.constructorArgumentLock) {
                if (mbd.resolvedConstructorOrFactoryMethod != null) {
                    resolved = true;
                    autowireNecessary = mbd.constructorArgumentsResolved;
                }
            }
        }
        if (resolved) {
            if (autowireNecessary) {
                return autowireConstructor(beanName, mbd, null, null);
            }
            else {
                return instantiateBean(beanName, mbd);
            }
        }

        //Instantiate using constructor
        Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
        if (ctors != null ||
                mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
                mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {
            return autowireConstructor(beanName, mbd, ctors, args);
        }

        //Instantiate the Bean with the default constructor (using default constructor)
        return instantiateBean(beanName, mbd);
    }

 

This process is mainly the generation of instantiation of Bean containing objects. There are three main methods:

• factory method
• constructor function instantiation
• default constructor instantiation (reflection and CGLIB methods are provided)
instantiateBean () is the most common method of Bean instantiation.

AbstractAutowireCapableBeanFactory.java

    /**
     * Instantiate the given bean using its default constructor.
     * @param beanName the name of the bean
     * @param mbd the bean definition for the bean
     * @return a BeanWrapper for the new instance
     */
    protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
        try {
            Object beanInstance;
            final BeanFactory parent = this;
            if (System.getSecurityManager() != null) {
                beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
                        getInstantiationStrategy().instantiate(mbd, beanName, parent),
                        getAccessControlContext());
            }
            else {
                //The default instantiation strategy is cglibsubclassing instantiation strategy, that is, CGLIB instantiates beans
                beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
            }
            //After instantiation, return the corresponding BeanWrapper class
            BeanWrapper bw = new BeanWrapperImpl(beanInstance);
            initBeanWrapper(bw);
            return bw;
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
        }
    }

 

The above uses the instantiation of the default constructor. In this process:

beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);

 

getInstantiationStrategy() returns cglibsubclassing instantiationstrategy by default, that is, CGLIB is used to instantiate beans by default.

AbstractAutowireCapableBeanFactory.java

//The returned instantiationStrategy defaults to cglibsubclassing instantiationStrategy
protected InstantiationStrategy getInstantiationStrategy() {
        return this.instantiationStrategy;
}

private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

 

Cglibsubclassing instantiationstrategy inherits the SimpleInstantiationStrategy class, so when understanding how CGLIB instantiates Bean objects, we first enter the SimpleInstantiationStrategy class to see what policies this instantiation strategy provides.

SimpleInstantiationStrategy.java

    @Override
    public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
        // Don't override the class with CGLIB if no overrides.
        if (!bd.hasMethodOverrides()) {
            Constructor<?> constructorToUse;
            synchronized (bd.constructorArgumentLock) {
                //Here, get the factory method of the specified constructor or object to instantiate the Bean
                constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
                if (constructorToUse == null) {
                    final Class<?> clazz = bd.getBeanClass();
                    if (clazz.isInterface()) {
                        throw new BeanInstantiationException(clazz, "Specified class is an interface");
                    }
                    try {
                        if (System.getSecurityManager() != null) {
                            constructorToUse = AccessController.doPrivileged(
                                    (PrivilegedExceptionAction<Constructor<?>>) () -> clazz.getDeclaredConstructor());
                        }
                        else {
                            constructorToUse =  clazz.getDeclaredConstructor();
                        }
                        bd.resolvedConstructorOrFactoryMethod = constructorToUse;
                    }
                    catch (Throwable ex) {
                        throw new BeanInstantiationException(clazz, "No default constructor found", ex);
                    }
                }
            }
            //Use BeanUtils (reflection) to instantiate the Bean. When specifically calling, instantiate the Bean through the constructor
            return BeanUtils.instantiateClass(constructorToUse);
        }
        else {
            // Instantiate beans using Cglib
            return instantiateWithMethodInjection(bd, beanName, owner);
        }
    }

 

SimpleInstantiationStrategy is the default class for Spring to generate Bean objects. Class provides two methods to instantiate Bean objects:

• BeanUtils (using JVM reflection)
  •  CGLIB
Let's see how these two methods of instantiating Bean objects are implemented:

BeanUtils
Through return BeanUtils instantiateClass(constructorToUse);
Enter the instantiateClass () method in the bean utils class.

BeanUtils.java

    /**
     * Convenience method to instantiate a class using the given constructor.
     * <p>Note that this method tries to set the constructor accessible if given a
     * non-accessible (that is, non-public) constructor, and supports Kotlin classes
     * with optional parameters and default values.
     * @param ctor the constructor to instantiate
     * @param args the constructor arguments to apply (use {@code null} for an unspecified
     * parameter if needed for Kotlin classes with optional parameters and default values)
     * @return the new instance
     * @throws BeanInstantiationException if the bean cannot be instantiated
     * @see Constructor#newInstance
     */
    public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
        Assert.notNull(ctor, "Constructor must not be null");
        try {
            //ReflectionUtils: using reflection, call the constructor to create a runtime object
            ReflectionUtils.makeAccessible(ctor);
            //(Kotlin? WTF, here is the relatively new source code...) Called ctor Newinstance (args), which uses the constructor
            return (KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
                    KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
        }
        catch (InstantiationException ex) {
            throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
        }
        catch (IllegalAccessException ex) {
            throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
        }
        catch (IllegalArgumentException ex) {
            throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
        }
        catch (InvocationTargetException ex) {
            throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
        }
    }

 

As you can see, BeanUtils uses reflection to call the constructor to create Bean objects.

CGLIB
Then enter the cglibsubclassing instantiationstrategy class to see how CGLIB instantiates beans.

CglibSubclassingInstantiationStrategy.java

        public Object instantiate(@Nullable Constructor<?> ctor, @Nullable Object... args) {
            //Generate bytecode using Enhancer
            Class<?> subclass = createEnhancedSubclass(this.beanDefinition);
            Object instance;
            // No constructor passed, instantiate directly
            if (ctor == null) {
                instance = BeanUtils.instantiateClass(subclass);
            }
            else {
                try {
                    // Instantiate object
                    Constructor<?> enhancedSubclassConstructor = subclass.getConstructor(ctor.getParameterTypes());
                    instance = enhancedSubclassConstructor.newInstance(args);
                }
                catch (Exception ex) {
                    throw new BeanInstantiationException(this.beanDefinition.getBeanClass(),
                            "Failed to invoke constructor for CGLIB enhanced subclass [" + subclass.getName() + "]", ex);
                }
            }
            // SPR-10785: set callbacks directly on the instance instead of in the
            // enhanced class (via the Enhancer) in order to avoid memory leaks.
            Factory factory = (Factory) instance;
            factory.setCallbacks(new Callback[] {NoOp.INSTANCE,
                    new LookupOverrideMethodInterceptor(this.beanDefinition, this.owner),
                    new ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner)});
            return instance;
        }

        //Generate the Enhancer object and set the parameters of the generated Java object for the Enhancer object, such as base class, callback method, etc
        private Class<?> createEnhancedSubclass(RootBeanDefinition beanDefinition) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(beanDefinition.getBeanClass());
            enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
            if (this.owner instanceof ConfigurableBeanFactory) {
                ClassLoader cl = ((ConfigurableBeanFactory) this.owner).getBeanClassLoader();
                enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(cl));
            }
            enhancer.setCallbackFilter(new MethodOverrideCallbackFilter(beanDefinition));
            enhancer.setCallbackTypes(CALLBACK_TYPES);
            return enhancer.createClass();
        }
    }

 

At this step, the process of Bean creation and instantiation is over. After Bean objects are generated, the dependencies of these Bean objects need to be set to complete the whole dependency injection process. This requires the populate () method.

2 dependency injection of object relationship

In the doCreateBean() method of class AbstractAutowireCapableBeanFactory, we have touched the populateBean() method:

AbstractAutowireCapableBeanFactory.java

//For Bean initialization, dependency injection generally occurs here.
Object exposedObject = bean;
try {
    //populateBean: Handling dependencies between objects
    populateBean(beanName, mbd, instanceWrapper);
    //initializeBean: initialization of a Bean
    exposedObject = initializeBean(beanName, exposedObject, mbd);
}

 

Now enter the populateBean method to see the specific implementation:

populateBean(): 
AbstractAutowireCapableBeanFactory.java

    protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
        if (bw == null) {
            if (mbd.hasPropertyValues()) {
                throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
            }
            else {
                // //If the instance object is null and the property value is empty, skip. Skip property population phase for null instance.
                return;
            }
        }

        boolean continueWithPropertyPopulation = true;
        //Post processor for calling Bean: BeanPostProcessor
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof InstantiationAwareBeanPostProcessor) {
                    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                    if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                        continueWithPropertyPopulation = false;
                        break;
                    }
                }
            }
        }

        if (!continueWithPropertyPopulation) {
            return;
        }

        //Get the attribute value of the container when parsing the Bean definition
        PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

        //Dependency injection is performed according to Autowire set by XML. Note: the Autowire here is different from the annotation @ Autowire
        if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
                mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
            MutablePropertyValues newPvs = new MutablePropertyValues(pvs);

            // AUTOWIRE_BY_NAME: automatically assemble according to Bean name and setter
            if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
                autowireByName(beanName, mbd, bw, newPvs);
            }

            // AUTOWIRE_BY_TYPE: automatically assemble according to Bean type
            if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
                autowireByType(beanName, mbd, bw, newPvs);
            }

            pvs = newPvs;
        }

        boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
        boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);

        if (hasInstAwareBpps || needsDepCheck) {
            if (pvs == null) {
                pvs = mbd.getPropertyValues();
            }
            PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
            if (hasInstAwareBpps) {
                for (BeanPostProcessor bp : getBeanPostProcessors()) {
                    if (bp instanceof InstantiationAwareBeanPostProcessor) {
                        InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                        pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                        if (pvs == null) {
                            return;
                        }
                    }
                }
            }
            if (needsDepCheck) {
                checkDependencies(beanName, mbd, filteredPds, pvs);
            }
        }

        if (pvs != null) {
            //Here is the place to inject attributes
            applyPropertyValues(beanName, mbd, bw, pvs);
        }
    }

 

According to the above code, in the populateBean () method, first use the Bean's post processor: BeanPostProcessor to process the Bean, and then obtain all the attribute values of the Bean. If the autowire="byName/byType" item is configured in the XML configuration file, it will be called
autowireByName(beanName, mbd, bw, newPvs);
and
autowireByType(beanName, mbd, bw, newPvs);

Dependency injection. Note that Autowire here is in the configuration file, which is different from the annotation @ Autowire. Since autowire="byName/byType" is not recommended in the configuration file, the two methods autowireByType and autowireByName are not analyzed.

Then, call the applyPropertyValues() method to inject properties:

AbstractAutowireCapableBeanFactory.java

    protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
        //pvs is the property value obtained
        if (pvs.isEmpty()) {
            return;
        }
        //Encapsulate property values with MutablePropertyValues
        MutablePropertyValues mpvs = null;
        List<PropertyValue> original;

        if (System.getSecurityManager() != null) {
            if (bw instanceof BeanWrapperImpl) {
                ((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
            }
        }

        if (pvs instanceof MutablePropertyValues) {
            mpvs = (MutablePropertyValues) pvs;
            //If mpvs has been converted, attribute injection is performed directly
            if (mpvs.isConverted()) {
                // Shortcut: use the pre-converted values as-is.
                try {
                    //Property injection, which will be analyzed later
                    bw.setPropertyValues(mpvs);
                    return;
                }
                catch (BeansException ex) {
                    throw new BeanCreationException(
                            mbd.getResourceDescription(), beanName, "Error setting property values", ex);
                }
            }
            original = mpvs.getPropertyValueList();
        }
        else {
            original = Arrays.asList(pvs.getPropertyValues());
        }

        //Need conversion attribute, get custom conversion
        TypeConverter converter = getCustomTypeConverter();
        if (converter == null) {
            converter = bw;
        }
        //Create an attribute parser to parse attributes
        BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);

        // Create a copy of the resolved value of the attribute and inject the copied data into the instance object
        List<PropertyValue> deepCopy = new ArrayList<>(original.size());
        boolean resolveNecessary = false;
        for (PropertyValue pv : original) {
            //No conversion required
            if (pv.isConverted()) {
                deepCopy.add(pv);
            }
            else {
                String propertyName = pv.getName();
                //Original value
                Object originalValue = pv.getValue();
                //The converted value: resolveValueIfNecessary. This is the resolution
                Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
                Object convertedValue = resolvedValue;
                boolean convertible = bw.isWritableProperty(propertyName) &&
                        !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
                if (convertible) {
                    convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
                }
                // Possibly store converted value in merged bean definition,
                // in order to avoid re-conversion for every created bean instance.
                if (resolvedValue == originalValue) {
                    if (convertible) {
                        pv.setConvertedValue(convertedValue);
                    }
                    deepCopy.add(pv);
                }
                else if (convertible && originalValue instanceof TypedStringValue &&
                        !((TypedStringValue) originalValue).isDynamic() &&
                        !(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
                    pv.setConvertedValue(convertedValue);
                    deepCopy.add(pv);
                }
                else {
                    resolveNecessary = true;
                    deepCopy.add(new PropertyValue(pv, convertedValue));
                }
            }
        }
        if (mpvs != null && !resolveNecessary) {
            mpvs.setConverted();
        }

        // Set our (possibly massaged) deep copy.
        try {
            //Inject the converted copied data into the object
            bw.setPropertyValues(new MutablePropertyValues(deepCopy));
        }
        catch (BeansException ex) {
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Error setting property values", ex);
        }
    }

In the applyPropertyValues() method, first check whether the property is MutablePropertyValues that meets the injection criteria. If so, start injection directly. Otherwise, you need to call the resolveValueIfNecessary () method to resolve the attribute.  
When do I need to parse?

<bean id="" class="">
    <property name="" ref=""/>
    <property name="">
        <props></..>
    </property >
    <property name="">
        <list></..>
    </property >
</bean>

 

When there are ref, props, list,map and other attributes in the Bean attributes of the configuration file, you need to call resolveValueIfNecessary() to resolve various attributes. Previously, this process was analyzed during the loading and parsing of BeanDefinition. If the Bean has ref, list,map and other attributes, it needs to be parsed into the corresponding RuntimeBeanReference, MangedList, MangedMap and other data structures. Now it needs to be parsed back.

Let's see how resolveValueIfNecessary() parses RuntimeBeanReference, MangedList, MangedMap and other data structures into internal structures that Spring can recognize.

BeanDefinitionValueResolver.java

    @Nullable
    public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
        //Resolve the attributes of the referenced type, corresponding to the ref attribute
        if (value instanceof RuntimeBeanReference) {
            RuntimeBeanReference ref = (RuntimeBeanReference) value;
            return resolveReference(argName, ref);
        }
        //Resolution of a reference to another Bean
        else if (value instanceof RuntimeBeanNameReference) {
            String refName = ((RuntimeBeanNameReference) value).getBeanName();
            refName = String.valueOf(doEvaluate(refName));
            if (!this.beanFactory.containsBean(refName)) {
                throw new BeanDefinitionStoreException(
                        "Invalid bean name '" + refName + "' in bean reference for " + argName);
            }
            return refName;
        }
        //The resolution of Bean type attributes is mainly the internal classes in the Bean
        else if (value instanceof BeanDefinitionHolder) {
            // Resolve BeanDefinitionHolder: contains BeanDefinition with name and aliases.
            BeanDefinitionHolder bdHolder = (BeanDefinitionHolder) value;
            return resolveInnerBean(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition());
        }
        else if (value instanceof BeanDefinition) {
            // Resolve plain BeanDefinition, without contained name: use dummy name.
            BeanDefinition bd = (BeanDefinition) value;
            String innerBeanName = "(inner bean)" + BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR +
                    ObjectUtils.getIdentityHexString(bd);
            return resolveInnerBean(argName, innerBeanName, bd);
        }
        //Property resolution of collection Array type (Array)
        else if (value instanceof ManagedArray) {
            // May need to resolve contained runtime references.
            ManagedArray array = (ManagedArray) value;
            Class<?> elementType = array.resolvedElementType;
            if (elementType == null) {
                String elementTypeName = array.getElementTypeName();
                if (StringUtils.hasText(elementTypeName)) {
                    try {
                        elementType = ClassUtils.forName(elementTypeName, this.beanFactory.getBeanClassLoader());
                        array.resolvedElementType = elementType;
                    }
                    catch (Throwable ex) {
                        // Improve the message by showing the context.
                        throw new BeanCreationException(
                                this.beanDefinition.getResourceDescription(), this.beanName,
                                "Error resolving array type for " + argName, ex);
                    }
                }
                else {
                    elementType = Object.class;
                }
            }
            return resolveManagedArray(argName, (List<?>) value, elementType);
        }
        //Property resolution of List type
        else if (value instanceof ManagedList) {
            // May need to resolve contained runtime references.
            return resolveManagedList(argName, (List<?>) value);
        }
        //Property resolution of type Set
        else if (value instanceof ManagedSet) {
            // May need to resolve contained runtime references.
            return resolveManagedSet(argName, (Set<?>) value);
        }
        //Attribute resolution of Map type
        else if (value instanceof ManagedMap) {
            // May need to resolve contained runtime references.
            return resolveManagedMap(argName, (Map<?, ?>) value);
        }
        //Property resolution of type Properties
        else if (value instanceof ManagedProperties) {
            Properties original = (Properties) value;
            Properties copy = new Properties();
            original.forEach((propKey, propValue) -> {
                if (propKey instanceof TypedStringValue) {
                    propKey = evaluate((TypedStringValue) propKey);
                }
                if (propValue instanceof TypedStringValue) {
                    propValue = evaluate((TypedStringValue) propValue);
                }
                if (propKey == null || propValue == null) {
                    throw new BeanCreationException(
                            this.beanDefinition.getResourceDescription(), this.beanName,
                            "Error converting Properties key/value pair for " + argName + ": resolved to null");
                }
                copy.put(propKey, propValue);
            });
            return copy;
        }
        //Property resolution of String type
        else if (value instanceof TypedStringValue) {
            // Convert value to target type here.
            TypedStringValue typedStringValue = (TypedStringValue) value;
            Object valueObject = evaluate(typedStringValue);
            try {
                Class<?> resolvedTargetType = resolveTargetType(typedStringValue);
                if (resolvedTargetType != null) {
                    return this.typeConverter.convertIfNecessary(valueObject, resolvedTargetType);
                }
                else {
                    return valueObject;
                }
            }
            catch (Throwable ex) {
                // Improve the message by showing the context.
                throw new BeanCreationException(
                        this.beanDefinition.getResourceDescription(), this.beanName,
                        "Error converting typed String value for " + argName, ex);
            }
        }
        else if (value instanceof NullBean) {
            return null;
        }
        else {
            return evaluate(value);
        }
    }

 

Among them, we can see how to resolve reference types and List types:

BeanDefinitionValueResolver.java

    @Nullable
    private Object resolveReference(Object argName, RuntimeBeanReference ref) {
        try {
            //Get the reference name from the RuntimeBeanReference, which is generated according to the configuration in the BeanDefinition
            Object bean;
            String refName = ref.getBeanName();
            refName = String.valueOf(doEvaluate(refName));
            //If ref is in the parent IoC container, look it up in the parent IoC container
            if (ref.isToParent()) {
                if (this.beanFactory.getParentBeanFactory() == null) {
                    throw new BeanCreationException(
                            this.beanDefinition.getResourceDescription(), this.beanName,
                            "Can't resolve reference to bean '" + refName +
                            "' in parent factory: no parent factory available");
                }
                //Find in current container
                bean = this.beanFactory.getParentBeanFactory().getBean(refName);
            }
            else {
                //Trigger dependency injection
                bean = this.beanFactory.getBean(refName);
                this.beanFactory.registerDependentBean(refName, this.beanName);
            }
            if (bean instanceof NullBean) {
                bean = null;
            }
            return bean;
        }
        catch (BeansException ex) {
            throw new BeanCreationException(
                    this.beanDefinition.getResourceDescription(), this.beanName,
                    "Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);
        }
    }

 BeanDefinitionValueResolver.java

    private List<?> resolveManagedList(Object argName, List<?> ml) {
        List<Object> resolved = new ArrayList<>(ml.size());
        for (int i = 0; i < ml.size(); i++) {
            //Recursion, parsing List elements
            resolved.add(
                    resolveValueIfNecessary(new KeyedArgName(argName, i), ml.get(i)));
        }
        return resolved;
    }

 

The rest of the analysis process is similar to the above, and will not be explained one by one here.

After completing the above steps, the value of the property element in the Bean has been parsed, and then the dependency injection process is carried out.

setPropertyValues(): Property injection into object

AbstractPropertyAccessor.java

    @Override
    public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
            throws BeansException {

        List<PropertyAccessException> propertyAccessExceptions = null;
        //Get attribute list
        List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ?
                ((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));
        for (PropertyValue pv : propertyValues) {
            try {
                // This is where the attribute is injected
                setPropertyValue(pv);
            }
            catch (NotWritablePropertyException ex) {
                if (!ignoreUnknown) {
                    throw ex;
                }
                // Otherwise, just ignore it and continue...
            }
            catch (NullValueInNestedPathException ex) {
                if (!ignoreInvalid) {
                    throw ex;
                }
                // Otherwise, just ignore it and continue...
            }
            catch (PropertyAccessException ex) {
                if (propertyAccessExceptions == null) {
                    propertyAccessExceptions = new LinkedList<>();
                }
                propertyAccessExceptions.add(ex);
            }
        }
        // If we encountered individual exceptions, throw the composite exception.
        if (propertyAccessExceptions != null) {
            PropertyAccessException[] paeArray =
                    propertyAccessExceptions.toArray(new PropertyAccessException[propertyAccessExceptions.size()]);
            throw new PropertyBatchUpdateException(paeArray);
        }
    }

 

As can be seen from the above code, first get the list of all attributes related to the Bean:
List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ?
((MutablePropertyValues) pvs).getPropertyValueList() :

Arrays.asList(pvs.getPropertyValues()));

Then inject each attribute:
setPropertyValue(pv);

Here, the final implementation of setPropertyValue() is in the subclass BeanWrapperImpl class of BeanWrapper (we mentioned BeanWrapper before, which is the encapsulation of beans to facilitate set and get operations):

BeanWrapperImpl.java

        @Override
        public void setValue(final @Nullable Object value) throws Exception {
            final Method writeMethod = (this.pd instanceof GenericTypeAwarePropertyDescriptor ?
                    ((GenericTypeAwarePropertyDescriptor) this.pd).getWriteMethodForActualAccess() :
                    this.pd.getWriteMethod());
            if (System.getSecurityManager() != null) {
                AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                    ReflectionUtils.makeAccessible(writeMethod);
                    return null;
                });
                try {
                    AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () ->
                            writeMethod.invoke(getWrappedInstance(), value), acc);
                }
                catch (PrivilegedActionException ex) {
                    throw ex.getException();
                }
            }
            else {
                ReflectionUtils.makeAccessible(writeMethod);
                //Use reflection to inject the value value through the set method
                writeMethod.invoke(getWrappedInstance(), value);
            }
        }
    }

You can see that after getting the attribute value value, you can use the reflection method Invoke() injects it. This completes all the procedures of IoC container dependency injection.

 

Topics: Spring source code