Spring Bean life cycle and extension point source code interpretation


1 Bean life cycle

Insert image description here

Insert image description here

In the Spring framework, the Bean object also has its life cycle. However, we are not very clear about the life cycle of the Bean object, because Spring helps us manage the Bean object. Therefore, we must master the life cycle of the Bean and know what Spring is doing every time. What this stage has done for us is very necessary.

The life cycle of a Bean is actually very simple. It is nothing more than the process from object creation to destruction. However, Spring, as an extensible framework, has added many extension points in the creation and destruction process of Bean. This is why One reason why Spring has been able to flourish to this day. The life cycle of a Bean can be roughly summarized into the following stages:

  • Bean definition
  • Bean registration
  • Bean creation
  • Bean injection
  • Bean initialization
  • Bean destruction

2 Bean definition, registration and creation process

The definition of the Bean is very simple and is done by us. For example, configuring a Bean in the Spring configuration file:

<bean id="user" class="com.wwj.entity.User">
    <property name="name" value="zs"/>
    <property name="age" value="22"/>
</bean>

Or a note:

@Component
public class User{
    
    
 private String name;
    private Integer age;
}

At this point, a Bean is defined. Next, Spring will register these defined Beans when it starts. For configuration file startup, Spring will parse the configuration in the configuration file to register the Bean, which is specifically reflected in the refresh method. :

@Override
public void refresh() throws BeansException, IllegalStateException {
    
    
    synchronized (this.startupShutdownMonitor) {
    
    
        // Prepare this context for refreshing.
        prepareRefresh();

        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);

        // Allows post-processing of the bean factory in context subclasses.
        postProcessBeanFactory(beanFactory);

        // Invoke factory processors registered as beans in the context.
        invokeBeanFactoryPostProcessors(beanFactory);

        // Register bean processors that intercept bean creation.
        registerBeanPostProcessors(beanFactory);

        // Initialize message source for this context.
        initMessageSource();

        // Initialize event multicaster for this context.
        initApplicationEventMulticaster();

        // Initialize other special beans in specific context subclasses.
        onRefresh();

        // Check for listener beans and register them.
        registerListeners();

        // Instantiate all remaining (non-lazy-init) singletons. 实例化Bean
        finishBeanFactoryInitialization(beanFactory);

        // Last step: publish corresponding event.
        finishRefresh();
    }
}

The finishBeanFactoryInitialization(beanFactory)method is used to instantiate all singleton beans. The source code of this method is as follows:

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    
    
    // Initialize conversion service for this context.
    if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
        beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
    
    
        beanFactory.setConversionService(
            beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
    }

    // Register a default embedded value resolver if no bean post-processor
    // (such as a PropertyPlaceholderConfigurer bean) registered any before:
    // at this point, primarily for resolution in annotation attribute values.
    if (!beanFactory.hasEmbeddedValueResolver()) {
    
    
        beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
    }

    // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
    String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
    for (String weaverAwareName : weaverAwareNames) {
    
    
        getBean(weaverAwareName);
    }

    // Stop using the temporary ClassLoader for type matching.
    beanFactory.setTempClassLoader(null);

    // Allow for caching all bean definition metadata, not expecting further changes.
    beanFactory.freezeConfiguration();

    // Instantiate all remaining (non-lazy-init) singletons.
    beanFactory.preInstantiateSingletons();
}

We will not analyze all methods in detail, but only look at the important parts. The beanFactory.preInstantiateSingletons()methods instantiate all singleton beans. Now that we know where the beans are created, how does Spring know which beans need to be created? In other words, where are the configuration files parsed? Let’s go back to the original refresh method. There is one of them ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(), which is used to parse the configuration file. The source code is as follows:

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    
    
    refreshBeanFactory();
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    if (logger.isDebugEnabled()) {
    
    
        logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
    }
    return beanFactory;
}

It calls refreshBeanFactory()the method:

@Override
protected final void refreshBeanFactory() throws BeansException {
    
    
    if (hasBeanFactory()) {
    
    
        destroyBeans();
        closeBeanFactory();
    }
    try {
    
    
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        beanFactory.setSerializationId(getId());
        customizeBeanFactory(beanFactory);
        loadBeanDefinitions(beanFactory);
        synchronized (this.beanFactoryMonitor) {
    
    
            this.beanFactory = beanFactory;
        }
    }
    catch (IOException ex) {
    
    
        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
}

This method in turn calls loadBeanDefinitions(beanFactory)the method:

@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    
    
    // Create a new XmlBeanDefinitionReader for the given BeanFactory.
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

    // Configure the bean definition reader with this context's
    // resource loading environment.
    beanDefinitionReader.setEnvironment(this.getEnvironment());
    beanDefinitionReader.setResourceLoader(this);
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

    // Allow a subclass to provide custom initialization of the reader,
    // then proceed with actually loading the bean definitions.
    initBeanDefinitionReader(beanDefinitionReader);
    loadBeanDefinitions(beanDefinitionReader);
}

Then call loadBeanDefinitions(beanDefinitionReader)the method:

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws{
    
    
    Resource[] configResources = getConfigResources();
    if (configResources != null) {
    
    
        reader.loadBeanDefinitions(configResources);
    }
    String[] configLocations = getConfigLocations();
    if (configLocations != null) {
    
    
        reader.loadBeanDefinitions(configLocations);
    }
}

That's almost it. The call stack is quite deep, so I won't go any further. Here we are parsing the xml file and creating a Bean instance.

3 Bean injection process

During the process of creating an object, we also need to assign values ​​to the properties of the object. So how does Spring implement it?

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
    
    
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
    
    
        instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName);
    }

    if (instanceWrapper == null) {
    
    
        instanceWrapper = this.createBeanInstance(beanName, mbd, args);
    }
    ......
        try {
    
    
            // 看这里
            this.populateBean(beanName, mbd, instanceWrapper);
            exposedObject = this.initializeBean(beanName, exposedObject, mbd);
        } catch (Throwable var18) {
    
    
        }

}

this.populateBean(beanName, mbd, instanceWrapper)The method is used to implement attribute assignment:

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 {
    
    
            // Skip property population phase for null instance.
            return;
        }
    }

    // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
    // state of the bean before properties are set. This can be used, for example,
    // to support styles of field injection.
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
    
    
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
    
    
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
    
    
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
    
    
                    return;
                }
            }
        }
    }

    PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

    int resolvedAutowireMode = mbd.getResolvedAutowireMode();
    if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
    
    
        MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
        // Add property values based on autowire by name if applicable.
        if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
    
    
            autowireByName(beanName, mbd, bw, newPvs);
        }
        // Add property values based on autowire by type if applicable.
        if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
    
    
            autowireByType(beanName, mbd, bw, newPvs);
        }
        pvs = newPvs;
    }

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

    PropertyDescriptor[] filteredPds = null;
    if (hasInstAwareBpps) {
    
    
        if (pvs == null) {
    
    
            pvs = mbd.getPropertyValues();
        }
        for
            if (bp instanceof

                PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
                if (pvsToUse == null) {
    
    
                    if (filteredPds == null) {
    
    
                        filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
                    }
                    pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                    ifnull) {
    
    
                        return;
                    }
                }
                pvs = pvsToUse;
            }
        }
    }
    if (needsDepCheck) {
    
    
        ifnull) {
    
    
            filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
        }
        checkDependencies(beanName, mbd, filteredPds, pvs);
    }

    if (pvs != null) {
    
    
        applyPropertyValues(beanName, mbd, bw, pvs);
    }
}

This method is very long, so I won’t walk you through it step by step. Interested students can read the source code themselves.

4 Bean destruction process

The destruction process is very simple. When the container's close method is called, Spring will automatically call the Bean's destruction method to implement the destruction logic.

5 Bean life cycle

The above content is only a general introduction to the Bean life cycle. In fact, Spring provides a lot of extension points throughout the life cycle. The specific process is as follows:

  • Create Bean instance
  • Call the setter() method in the Bean to set the attribute value
  • Check whether the Bean implements the Aware interface. If so, call the corresponding interface method.
  • If there is a BeanPostProcessor in the container, call its postProcessAfterInitialization
  • Check whether the Bean implements InitializingBean. If so, call its afterPropertiesSet method.
  • Check whether the Bean's init-method attribute is specified. If so, call the specified method.
  • If there is a BeanPostProcessor in the container, call its postProcessorAfterInitialization
  • Check whether the Bean implements DisposableBean, and if so, call its method
  • Check whether the destroy-method attribute of the Bean is specified. If so, call the specified method.

We can test it:

public class User implements ApplicationContextAware, InitializingBean, DisposableBean {
    
    

    private
    private

    public User() {
    
    
        System.out.println("1--》创建User实例");
    }

    public void setName(String name) {
    
    
        this.name = name;
"2--》设置User的name属性");
    }

    public void setAge(Integer age) {
    
    
        this.age = age;
"2--》设置User的age属性");
    }

    public void init() {
    
    
"6--》调用init-method属性指定的方法");
    }

    public void myDestroy() {
    
    
"9--》调用destroy-method属性指定的方法");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    
    
"3--》调用对应Aware接口的方法");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
    
    
"5--》调用InitializingBean接口的afterPropertiesSet方法");
    }

    @Override
    public void destroy() throws Exception {
    
    
"8--》调用DisposableBean接口的destroy方法");
    }
}

This Bean implements some extension points provided by Spring, including ApplicationContextAware, InitialzingBean, DisposableBean, etc., so let's write a Bean post-processor:

public class MyBeanPostProcessor implements BeanPostProcessor {
    
    

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    
    
        System.out.println("7--》调用MyBeanPostProcessor的postProcessBeforeInitialization方法");
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    
    
        System.out.println("4--》调用MyBeanPostProcessor的postProcessAfterInitialization方法");
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }
}

Finally, register them in the container and specify the initialization and destruction methods corresponding to the beans:

@Configuration
public class MyBeanConfig {
    
    

    @Bean(initMethod = "init", destroyMethod = "myDestroy")
    public User user() {
    
    
        User user = new User();
        user.setName("zs");
        user.setAge(30);
        return user;
    }

    @Bean
    public BeanPostProcessor beanPostProcessor() {
    
    
        return new MyBeanPostProcessor();
    }
}

The running results are as follows:

1--》创建User实例
2--》设置User的name属性
2--》设置User的age属性
3--》调用对应Aware接口的方法
4--》调用MyBeanPostProcessor的postProcessAfterInitialization方法
5--》调用InitializingBean接口的afterPropertiesSet方法
6--》调用init-method属性指定的方法
7--》调用MyBeanPostProcessor的postProcessBeforeInitialization方法
8--》调用DisposableBean接口的destroy方法
9--》调用destroy-method属性指定的方法

As we expected, Spring called each extension point in turn. After becoming familiar with the entire Bean life cycle and extension points, we can do what we want to do at each stage and achieve business customization.

Guess you like

Origin blog.csdn.net/ZGL_cyy/article/details/132867153