Antecedents to review

The previous articles focused on how the Spring IoC container initializes and parses and registers the bean information we define.

“IoC container in Spring” provides an overview of containers in Spring. “Spring IoC Container Initialization” and “Spring IoC Container Initialization (2)” analyze how Spring initializes IoC containers. “How does Spring parse the

tag?” We analyzed how Spring parses the

tag and its child tags and registers them with the BeanFactory.

The main process is as follows:

The IoC container has been created, and the bean information we defined has been put into the container, so how do we get objects from the container?

This paper continues the analysis.

Configure and test the code

The bean configuration file and test code are pasted here for ease of review.

  • The configuration file application-ioc.xml

      
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="person" class="com.jaxer.spring.ioc.Person">
        <property name="pet" ref="dog"/>
    </bean>

    <bean id="dog" class="com.jaxer.spring.ioc.Dog">
        <property name="age" value="1"/>
        <property name="owner" ref="person"/>
    </bean>

</beans>
Copy the code
  • The test code
public class IocTests {
    @Test
    public void test01(a) {
        ApplicationContext context = new ClassPathXmlApplicationContext("application-ioc.xml");
        System.out.println(context.getBean("person"));
        System.out.println(context.getBean("dog")); }}/ output: * * * Person {id = 12, name = 'Jack - 12'} * Dog {age = 1} * /
Copy the code

How do I get objects from a container?

Getting objects from the container is done through the BeanFactory#getBean method, which has multiple overloaded methods, but ultimately passes

Abstractbeanfactor #doGetBean method. The doGetBean method code is as follows:

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
    // ...

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

        String beanName = transformedBeanName(name);
        Object bean;

        // Get the singleton bean object from the cache
        Object sharedInstance = getSingleton(beanName);
        if(sharedInstance ! =null && args == null) {
            // ...
            // Process the FactoryBean scenario
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        }
        
        // There is no bean object in the cache
        else {
            if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }

            // If the bean object is in the parent container, the bean object is fetched from the parent container
            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) {
                    // Delegation to parent with explicit args.
                    return (T) parentBeanFactory.getBean(nameToLookup, args);
                }
                else if(requiredType ! =null) {
                    // No args -> delegate to standard getBean method.
                    return parentBeanFactory.getBean(nameToLookup, requiredType);
                }
                else {
                    return(T) parentBeanFactory.getBean(nameToLookup); }}// Do type checking only
            if(! typeCheckOnly) { markBeanAsCreated(beanName); }try {
                / / get BeanDefinition
                RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                checkMergedBeanDefinition(mbd, beanName, args);

                // Get the dependent bean object
                // When creating a bean object that depends on other objects, create the dependent object first
                String[] dependsOn = mbd.getDependsOn();
                if(dependsOn ! =null) {
                    for (String dep : dependsOn) {
                        if (isDependent(beanName, dep)) {
                            // ...
                        }
                        registerDependentBean(dep, beanName);
                        try {
                            getBean(dep);
                        }
                        catch (NoSuchBeanDefinitionException ex) {
                            // ...}}}// Create an object whose scope is singleton
                if (mbd.isSingleton()) {
                    sharedInstance = getSingleton(beanName, () -> {
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            // ...}});// Process the FactoryBean scenario
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }

                // Create an object whose scope is prototype
                else if (mbd.isPrototype()) {
                    // It's a prototype -> create a new instance.
                    Object prototypeInstance = null;
                    try {
                        beforePrototypeCreation(beanName);
                        prototypeInstance = createBean(beanName, mbd, args);
                    }
                    finally {
                        afterPrototypeCreation(beanName);
                    }
                    // Process the FactoryBean scenario
                    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                }

                // Create an object of another type
                else {
                    String scopeName = mbd.getScope();
                    if(! StringUtils.hasLength(scopeName)) {throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
                    }
                    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 {
                                return createBean(beanName, mbd, args);
                            }
                            finally{ afterPrototypeCreation(beanName); }});// Process the FactoryBean scenario
                        bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                    }
                    catch (IllegalStateException ex) {
                        // ...}}}catch (BeansException ex) {
                cleanupAfterBeanCreationFailure(beanName);
                throwex; }}// Type check
        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) {
                // ...}}return(T) bean; }}Copy the code

The main way to get a bean object is through this doGetBean method.

This method may seem a bit long, but its internal implementation is longer and more complex. But there are signs. Don’t panic.

This paper first looks at the overall flow of this method, and then slowly study the internal logic. Start with the flow chart:

The code is a bit long, but it’s not that complicated when you tease it out.

What does this method basically do?

When a bean object is fetched from the container, it is first fetched from the cache. If present in the cache, process the FactoryBean scenario.

BeanFactory and FactoryBean, both of which look very similar, have specific interview questions you might ask.

HMM… Do you have a chance to analyze it separately?

If you don’t have one in the cache, go to the parent container first, and you can specify the parent parameter when you create the BeanFactory, which is that one.

Outside the parent container, if the bean object depends on other objects, the dependent bean object is created first, and then the corresponding bean object is created according to the scope attribute of the <bean> tag.

Isn’t it a bit like when we write the query interface, we first query from the cache, and then query the DB if there is nothing in the cache?

It’s the same thing, space for time.

summary

First the whole, then the details.

This article starts with a brief overview of how to get bean objects from the Spring IoC container, which will be broken down later.

Take a break ~