Spring principle and source code analysis series - Spring IoC container startup process analysis

preface

Spring's IoC container is a lightweight container that provides IoC support.  
BeanFactory provides two containers: BeanFactory and BeanFactory ApplicationContext.  
The inheritance relationship between the two is as follows:

 

 

• BeanFactory: the basic IoC container adopts the lazy load strategy by default, that is, only when the client object needs to access a bean object in the container, will the bean object be initialized and dependency injected. Therefore, BeanFactory container is characterized by fast startup speed and limited resources. It is suitable for scenes with limited resources and lax functional requirements.

• ApplicationContext: ApplicationContext is built on the basis of BeanFactory and supports other advanced features, such as internationalization, event publishing, etc.
Compared with BeanFactory container, ApplicationContext completes the initialization of resources when it is started, so it takes a long time to start. It is suitable for scenes with sufficient system resources and more functions.

This chapter is divided into two articles to describe the analysis of Spring container startup process. The first chapter mainly introduces the related concepts of Bean in Spring and IoC container types; The second chapter begins with a detailed introduction to the startup process of IoC container.
This chapter details the related concepts of Bean in Spring and IoC container types.

1, Spring Bean

Before introducing IoC container, we need to know what Bean is and related concepts.

1. Bean definition

Definition of Bean in Java:

All attributes in the class must be encapsulated, that is, use private declaration;
If the encapsulated attribute needs to be operated externally, the corresponding setter and getter methods must be written;
At least one parameterless constructor exists in a JavaBean.
For example:

package com.wgs.spring.bean;
/**
 * @author GenshenWang.nomico
 * @date 2017/11/23.
 */
public class User {
    private String name;
    private int age;
    public void setAge(int age) {
        this.age = age;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
}

The Spring IoC container is the factory that manages bean s.  
A Bean in spring is an object that is instantiated, assembled, and managed through the Spring IoC container. These beans are created using the configuration metadata provided by the container. Spring can use XML configuration files to manage and configure Bean information, as follows

<?xml version="1.0" encoding="UTF-8"?>
<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="user" class="com.wgs.spring.bean.User"></bean>
</beans>

 

< beans > is the root node in the XML configuration file, which can contain multiple < bean > child nodes.
The configuration in Spring's XML configuration file corresponds to the < bean > element one by one.

Let's take a look at the common properties of bean definitions:
(1) id attribute
Generally, the object registered to the container has a unique ID value, such as id="user", which distinguishes the bean it represents from other beans. Of course, it can also be identified with the following name attribute value;
(2) name attribute
You can use the name attribute to specify the alias of the bean.
For example:

<bean id="user" name="beanname/user" class="com.wgs.spring.bean.User"></bean>

 

name can be separated by commas, spaces, colons, etc. to specify multiple names, but id cannot.  
(3) class attribute
Each bean registered to the container needs to specify its type through the class attribute. (some exceptions)

Other common attributes:

 

 

2. Type of Bean

  • XML
  • Annotation
  • Class
  • Properties,YML

3. Bean injection method

In XML configuration, two methods are commonly used: construction method injection and setter injection.
(1) Construction method injection
Constructor injection is to inject attribute values through a parameterized constructor method, and assign values to them through < constructor Arg type = "" value = "" > in XML.
For example:

public class User {
    private String name;
    private int age;

    public User(String name){
        this.name = name;
    }
    public User(int age){
        this.age = age;
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

In this way, in Xml, you can specify the parameter type to be passed in through the type attribute.  
For example:
If you assign a value to name, you only need to pass in a value of type = "String":

<bean id="user" class="com.wgs.spring.bean.User">
        <constructor-arg type="java.lang.String">
            <value>wgs</value>
        </constructor-arg>
</bean>

If you assign a value to age, you only need to pass in a value of type = "int":

<bean id="user" class="com.wgs.spring.bean.User">
        <constructor-arg type="int">
            <value>25</value>
        </constructor-arg>
</bean>

If you assign values to age and name at the same time, you can specify the order of incoming parameters through index:

<bean id="user" class="com.wgs.spring.bean.User">
    <constructor-arg index="0" value="wgs"></constructor-arg>
    <constructor-arg index="1" value="25"></constructor-arg>
</bean>

 

In this way, the value value of index = "0" is assigned to the first parameter value of constructor User(String name, int age);
The value value of index = "1" is assigned to the second parameter value of the constructor

If there are dependencies of other objects in the constructor, you can assign values through the ref attribute.

(2) setter method injection
Setter method injection requires Bean class to provide setter method and parameterless constructor, and assign value to it through < property name = "" value = "" > in XML configuration file.

public class User {
    private String name;
    private int age;

    public void setAge(int age) {
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

The < property > element is provided for the setter method in XML< The property > element has a name attribute, which is used to specify the attribute value in the corresponding bean class.  
As follows:

 <bean id="user" class="com.wgs.spring.bean.User">
       <property name="age" value="25"></property>
       <property name="name" value="wgs"></property>
</bean>

 

4. Bean's scope

Scope is often called "scope", which declares the lifetime of the object in the container. Beyond this scope, the object will be destroyed.
Common scope s of bean s are:

  • singleton
  • prototype
  • request
  • session
  • global session
The default scope of the container is singleton.
The following two types are commonly used:
(1)singleton
Beans declared as singleton have the following characteristics in the container:

An object instance: a singleton type bean has only one shared instance in a container, and all references to this type of bean will share this single instance.
Object instance lifetime: from the time the container is started to the time it is first requested and instantiated, it will survive until the container exits. That is, the life cycle of the IoC container is the same.
The default scope in IoC container is singleton, which can also be set to false in XML:

<bean id="user" name="beanname/user" class="com.wgs.spring.bean.User" 
scope="singleton"></bean>

 

(2) prototype

Beans declared as prototype have the following characteristics in the container:

• object instance: when receiving the request of this type of object, the container will re create a new object instance to the requester every time;
• life cycle: every time the container returns a new object instance to the requestor, it no longer has a reference to the object; The life and death of the object is the responsibility of the requesting party.
For object types that the requester cannot share, the scope defined by its bean should be set to prototype.

<bean id="user" name="beanname/user" class="com.wgs.spring.bean.User" scope="prototype"></bean>

 

In this way, each requester can get its own corresponding object instance.

The difference between singleton and prototype:
(1) For singleton type objects, only one object instance will exist in the container and be shared;
prototype objects create new object instances each time.
(2) The life cycle of singleton type objects is managed by the container;
The life cycle of prototype type objects is managed by themselves.

5. BeanDefinition interface

Spring IoC is a container for managing beans. It is responsible for creating, assembling and destroying beans.
In IoC container, BeanDefinition abstracts the definition of Bean and saves the necessary information of Bean. For example, in XML configuration, BeanDefinition saves the id,name,aliases and other attributes related to < Bean >, and encapsulates a lot of basic data related to Bean (in XML configuration, these data are configured through tags such as < Bean id = "" name = "" >), It is the core data structure of the container to realize the dependency inversion function.

The following is the source code of BeanDefinition:

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
    String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
    String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
    int ROLE_APPLICATION = 0;
    int ROLE_SUPPORT = 1;
    int ROLE_INFRASTRUCTURE = 2;
    String getParentName();
    void setParentName(String parentName);
    String getBeanClassName();
    void setBeanClassName(String beanClassName);
    String getFactoryBeanName();
    void setFactoryBeanName(String factoryBeanName);
    String getFactoryMethodName();
    void setFactoryMethodName(String factoryMethodName);
    String getScope();
    void setScope(String scope);
    boolean isLazyInit();
    void setLazyInit(boolean lazyInit);
    String[] getDependsOn();
    void setDependsOn(String... dependsOn);
    boolean isAutowireCandidate();
    void setAutowireCandidate(boolean autowireCandidate);
    boolean isPrimary();
    void setPrimary(boolean primary);
    ConstructorArgumentValues getConstructorArgumentValues();
    MutablePropertyValues getPropertyValues();
    boolean isSingleton();
    boolean isPrototype();
    boolean isAbstract();
    int getRole();
    String getDescription();
    String getResourceDescription();
    BeanDefinition getOriginatingBeanDefinition();
}

 

You can see that many bean related properties and methods are defined in the BeanDefinition interface,
Such as class name, scope, attribute, construction parameter list, whether single instance, whether soft loading, etc.
This operation on beans is actually a direct operation on BeanDefinition.

The BeanDefinition interface inherits the AttributeAccessor interface, which indicates that it has the ability to process attributes;
The BeanDefinition interface inherits the BeanMetadataElement interface, indicating that it has the attributes of the bean element.

BeanDefinition is just an interface. Common implementation classes are:

  • ChildBeanDefinition
  • RootBeanDefinition
  • GenericBeanDefinition
II. Spring IoC container - BeanFactory container

1 BeanFactory container responsibilities - object registration and dependency binding

Bean factory is the factory that generates beans. As the basic IoC container provided by Spring, its main responsibilities are:

• registration of business objects
• binding of dependencies between objects
Source code of BeanFactory:

package org.springframework.beans.factory;

import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;

public interface BeanFactory {

String FACTORY_BEAN_PREFIX = "&";
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansException;

Object getBean(String name, Object... args) throws BeansException;  
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

boolean containsBean(String name);

boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;    
Class<?> getType(String name) throws NoSuchBeanDefinitionException;

String[] getAliases(String name);

}

 

It can be seen that a series of methods for managing beans are defined in the BeanFactory interface, such as:
• getBean: get a Bean in the container according to the Bean name;
• containsBean: judge whether a Bean exists in the container;
• isSingleton: judge whether the Bean is a Singleton type Bean;
• isprotype: judge whether a Bean is a Prototype type Bean;
• isTypeMatch: judge whether the Class type of the Bean matches the specified Class type;
• getType: query the Class type of the Bean;
• getAliases: query the alias of the Bean.
...

BeanFactory is just an interface, and DefaultListableBeanFactory is a more general implementation class.
The following figure shows the interfaces involved in the process of BeanFactory container object registration and dependency binding:

 

 

As shown in the figure, DefaultListableBeanFactory not only indirectly implements the BeanFactory interface, but also implements the BeanDefinitionRegistry interface.
The BeanDefinitionRegistry interface definition abstracts the registration logic of beans and plays the role of Bean registration management. BeanDefinitionRegistry registers beans into containers and saves them in BeanDefinition. In the container, each managed Bean has a corresponding BeanDefinition, which saves all the necessary information of the object.

Summary:
• BeanFactory: this interface only defines how to access the beans managed in the container;
• BeanDefinitionRegistry: this interface plays the role of Bean registration management;
• DefaultListableBeanFactory: the specific implementation class of the above two interfaces, which is responsible for the management of Bean registration and management.

2. Object registration and dependency binding

The previous section describes the functions and responsibilities of BeanFactory, mainly object registration and dependency binding.
Spring provides three ways to implement object registration and dependency binding.

(1) Direct coding method

First, you need to create an IoC container through BeanFactory,
Then register the bean into the container through BeanDefinitionRegistry (and DefaultListableBeanFactory is the specific implementation class of the above two interfaces),
Finally, the dependency binding of the relationship between objects can be completed by construction injection or setter injection.

The code implementation is as follows:

package com.wgs.spring.registry;

import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;

/**
 * @author GenshenWang.nomico
 * @date 2017/11/17
 */
public class BeanFactoryRegistryDemo {

    public static void main(String[] args) {
        DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory();
        BeanFactory container = bindBeanByCode(beanRegistry);

        //3 after completing the above steps, you can get the Bean instance from the container
        CommentService service = (CommentService) container.getBean("commentService");
        System.out.println(service.getCount());
    }

    public static BeanFactory bindBeanByCode(BeanDefinitionRegistry beanRegistry){
        //First, abstract the Bean into BeanDefinition
        AbstractBeanDefinition commentService = new RootBeanDefinition(CommentService.class);
        AbstractBeanDefinition commentDao = new RootBeanDefinition(CommentDao.class);

        //1 registration: beanRegistry is an implementation class of BeanDefinitionRegistry, which can realize the function of Bean registration
        beanRegistry.registerBeanDefinition("commentService", commentService);
        beanRegistry.registerBeanDefinition("commentDao", commentDao);

        //2. Dependency binding: bind commnetDao to commnetService
        //  (1) Injection by construction method
       /* ConstructorArgumentValues argumentValues = new ConstructorArgumentValues();
        argumentValues.addIndexedArgumentValue(0, commentDao);
        commentService.setConstructorArgumentValues(argumentValues);*/
        // (2) Inject through setter method
        MutablePropertyValues propertyValues = new MutablePropertyValues();
        propertyValues.addPropertyValue(new PropertyValue("commentDao", commentDao));
        commentService.setPropertyValues(propertyValues);

        return (BeanFactory)beanRegistry;
    }
}

 

(2) External profile mode

Spring's IoC container supports two configuration file formats:

• Properties file format
• XML file format
In this process, there is a very important interface: BeanDefinitionReader.
The interface is responsible for reading the content of the configuration file and mapping it to BeanDefinition, and then handing the BeanDefinition to BeanDefinitionRegistry to complete the registration and loading of beans.

This paper describes this function through XML file format, and the code implementation is as follows:
xml configuration file:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="commentDao" class="com.wgs.spring.registry.CommentDao"/>
    <bean id="commentService" class="com.wgs.spring.registry.CommentService">
        <property name="commentDao">
            <ref bean="commentDao"></ref>
        </property>
    </bean>

</beans>

Spring provides the implementation class XmlBeanDefinitionReader of BeanDefinitionReader to read the file content and load it into the container:

package com.wgs.spring.registry;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.support.*;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;

/**
 * @author GenshenWang.nomico
 * @date 2017/11/17
 */
public class BeanFactoryRegistryDemo2 {

    public static void main(String[] args) {
        DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory();
        BeanFactory container = bindBeanByXML(beanRegistry);
        CommentService commentService = (CommentService) container.getBean("commentService");
        System.out.println(commentService.getCount());
    }

    public static BeanFactory bindBeanByXML(BeanDefinitionRegistry beanRegistry){
        //Read the content of the configuration file, parse the file format, map it to the corresponding BeanDefinition, and complete the registration
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanRegistry);
        reader.loadBeanDefinitions("classpath:/spring.xml");
        return (BeanFactory) beanRegistry;
    }
}

 

3) Annotation method

The annotation method requires @ Autowired and @ Component to mark the object.

@Component: used in conjunction with < context: component scan base package = "" / >, < context: component scan package = "" / > will scan the classes marked with @ component under the specified package and add them to the container as beans for management;
@Autowired: notifies the container to inject dependent objects into the current object.
The code implementation is as follows:

Component
public class CommentDao {
...
}

Component
public class CommentService {
    @Autowired
    private CommentDao commentDao;

    public int getCount(){
        commentDao = new CommentDao();
        return commentDao.getCommentCount();
    }
}

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

<context:component-scan base-package="com.wgs.spring.registry"/>
</beans>

 

3, Spring IoC container - ApplicationContext container

ApplicationContext is the implementation of advanced IoC container. In addition to all the functions supported by BeanFactory, it also further expands the functions of the basic container, including:

  • BeanFactoryPostProcessor
• automatic identification of special types of bean s
• automatic initialization of bean instances after container startup
• internationalization
• in container time release
Spring provides an XmlBeanFactory implementation for BeanFactory; Accordingly, the following common implementations are provided for ApplicationContext type containers:
(1) FileSystemXmlApplicationContext: load bean definitions and ApplicationContext implementation of related resources from the file system;
(2) ClassPathXmlApplicationContext: load the bean definition and the ApplicationContext implementation of related resources from the classpath;
(3) XmlWebApplicationContext: loads the bean definition and the ApplicationContext implementation of related resources from the Web application.

 

Keywords: Spring source code

Added by tdelobe on Thu, 10 Mar 2022 05:06:05 +0200