Source code analysis: Spring source code analysis notes (IV) startup process (II)

Time:2021-12-30

This paper is organized by colodoo (paper umbrella)
QQ 425343603
Java learning exchange group (717726984)

The current spring source code version is 5.2 10.RELEASE

Continue the previous articleSource code analysis: Spring source code analysis notes (III) startup process (in progress)

In the last article, we learned how the beandefinition we defined is instantiated, but it’s not all. It’s not the bean object we usually use. Let’s continue to read the source code to see how the bean is instantiatedAttribute fillingAnd completeinitializationof

Finish refresh

org.springframework.context.support.AbstractApplicationContext#finishRefresh

protected void finishRefresh() {
    // Clear context-level resource caches (such as ASM metadata from scanning).
    clearResourceCaches();

    //Initialize the lifecycle processor for this context.
    initLifecycleProcessor();

    //Get lifecycle
       //Refresh lifecycle
    //First, the refresh is propagated to the lifecycle processor
    getLifecycleProcessor().onRefresh();

    //Release final activity
    publishEvent(new ContextRefreshedEvent(this));

    //Participate in livebeansview MBeans (if active)
    LiveBeansView.registerApplicationContext(this);
}

Initialize lifecycle processor

org.springframework.context.support.AbstractApplicationContext#initLifecycleProcessor

protected void initLifecycleProcessor() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    //Get lifecycle processor
    if (beanFactory.containsLocalBean(LIFECYCLE_PROCESSOR_BEAN_NAME)) {
        //Get this setting into the current object lifecycle processor
        this.lifecycleProcessor =
            beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class);
        if (logger.isTraceEnabled()) {
            logger.trace("Using LifecycleProcessor [" + this.lifecycleProcessor + "]");
        }
    }
    else {
        //Initialize default lifecycle processor
        DefaultLifecycleProcessor defaultProcessor = new DefaultLifecycleProcessor();
        //Set the bean factory object for the lifecycle processor
        defaultProcessor.setBeanFactory(beanFactory);
        //Set lifecycle processor
        this.lifecycleProcessor = defaultProcessor;
        //Register the lifecycle processor into the bean factory
        beanFactory.registerSingleton(LIFECYCLE_PROCESSOR_BEAN_NAME, this.lifecycleProcessor);
        if (logger.isTraceEnabled()) {
            logger.trace("No '" + LIFECYCLE_PROCESSOR_BEAN_NAME + "' bean, using " +
                         "[" + this.lifecycleProcessor.getClass().getSimpleName() + "]");
        }
    }
}

Summarize this code

  • Gets the lifecycle processor that exists in the container. If it exists, it is set. If it does not exist, it is initialized.
  • Initialize default lifecycle processor
  • The default lifecycle processor is registered in the bean factory.

As the name suggests, this step is to prepare the lifecycle processor needed for our lifecycle initialization, and then we will really enter the bean lifecycle.

Lifecycle refresh (onrefresh)

org.springframework.context.support.DefaultLifecycleProcessor#onRefresh

public void onRefresh() {
    startBeans(true);
    this.running = true;
}

Enter this method and find the specific implementation logic instartBeansMethod, read on.

Start beans (startbeans)

org.springframework.context.support.DefaultLifecycleProcessor#startBeans

private void startBeans(boolean autoStartupOnly) {
    //Get lifecycle bean object (multiple)
    Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
    //Stage
    Map<Integer, LifecycleGroup> phases = new HashMap<>();
    //Traverse all lifecycle objects
    lifecycleBeans.forEach((beanName, bean) -> {
        // 
        if (!autoStartupOnly || (bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())) {
            int phase = getPhase(bean);
            LifecycleGroup group = phases.get(phase);
            if (group == null) {
                group = new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly);
                phases.put(phase, group);
            }
            group.add(beanName, bean);
        }
    });
    if (!phases.isEmpty()) {
        List<Integer> keys = new ArrayList<>(phases.keySet());
        Collections.sort(keys);
        for (Integer key : keys) {
            phases.get(key).start();
        }
    }
}

Summarize this Code:

  • Get lifecycle beans (getlifecycle beans)
  • Meanwhile, the lifecycle bean instantiation (getlifecyclebeans) is performed

Let’s continue to read the getlifecycle beans method to see if there is anything we want.

Get lifecycle bean (getlifecycle beans)

org.springframework.context.support.DefaultLifecycleProcessor#getLifecycleBeans

protected Map<String, Lifecycle> getLifecycleBeans() {
    //Get bean factory object
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    //Initialize beans
    Map<String, Lifecycle> beans = new LinkedHashMap<>();
    //Get all lifecycle objects
    //The value obtained here is lifecycle processor
    String[] beanNames = beanFactory.getBeanNamesForType(Lifecycle.class, false, false);
    for (String beanName : beanNames) {
        //Gets the bean name used for registration
        //Output - > lifecycle processor by default
        String beanNameToRegister = BeanFactoryUtils.transformedBeanName(beanName);
        //Determine whether it is a factory bean
        //The default output here is - > false
        boolean isFactoryBean = beanFactory.isFactoryBean(beanNameToRegister);
        //If it is a factory bean, add an "&" sign to the initial letter
        //This is not factory bean output - > lifecycle processor
        String beanNameToCheck = (isFactoryBean ? BeanFactory.FACTORY_BEAN_PREFIX + beanName : beanName);
        //Determine whether the object exists
        if ((beanFactory.containsSingleton(beanNameToRegister) &&
             (!isFactoryBean || matchesBeanType(Lifecycle.class, beanNameToCheck, beanFactory))) ||
            matchesBeanType(SmartLifecycle.class, beanNameToCheck, beanFactory)) {
            //Instantiate the object
            Object bean = beanFactory.getBean(beanNameToCheck);
            if (bean != this && bean instanceof Lifecycle) {
                beans.put(beanNameToRegister, (Lifecycle) bean);
            }
        }
    }
    //Returns the instantiated lifecycle object
    return beans;
}

This step is obvious. The previous step is to obtain the bean name of the lifecycle object.

See what we are familiar withbeanFactory.getBean, indicating that it instantiates the lifecycle bean, and finally returns the instantiated lifecycle object.

beanFactory.getBeanThis part of the source code, you can read the previous articleSource code analysis: Spring source code analysis notes (III) startup process (in progress)

After reading this, we didn’t find the place of attribute injection. Instead, we looked to see if we knew what was missing.

After my debugging, I found that we need to modify our code, because I found that our code does not have dependencies, and our userservice does not depend on any other beans.

We create a new usercontroller to call it and rely on the userservice bean.

Transformation code

UserController

package com.zhisan.spring.controller;

import com.zhisan.spring.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

public class UserController {

    private UserService userService;

    public void login() {
        userService.login();
    }

    public UserService getUserService() {
        return userService;
    }

    public void setUserService(UserService userService) {
        this.userService = userService;
    }
}

MainXml.java

package com.zhisan.spring;

import com.zhisan.spring.config.Config;
import com.zhisan.spring.controller.UserController;
import com.zhisan.spring.service.UserService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainXml {

    public static void main(String[] args) {

        //XML mode
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");

        UserController userController = applicationContext.getBean("userController", UserController.class);
        userController.login();

    }
}

spring.xml

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

    <bean id="userController" class="com.zhisan.spring.controller.UserController">
        <property name="userService" ref="userService"></property>
    </bean>
    <bean id="userService" class="com.zhisan.spring.service.UserService"> </bean>

</beans>

After the transformation, we continue to debug.

Set a breakpoint on the populatebean method below to facilitate debugging.

Because the process of finding the entrance is relatively long, the space in the middle is omitted.

Populatebean

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { 
   
    //Some irrelevant logic codes are omitted here

    //The attribute values in the class will be obtained here
    PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

    //Omit some check codes

    //Judge whether the attribute value list is empty. If it is not empty, enter the attribute application
    if (pvs != null) {
        applyPropertyValues(beanName, mbd, bw, pvs);
    }
}

This part of the code mainly completes some acquisition and verification of attribute filling. The real filling attribute value logic isapplyPropertyValues, let’s continue to read this method.

Apply property values (applypropertyvalues)

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyPropertyValues

protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
    //Judge whether it is empty
    if (pvs.isEmpty()) {
        return;
    }

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

    //Variable preparation
    MutablePropertyValues mpvs = null;
    List<PropertyValue> original;

    //Judgment type
    if (pvs instanceof MutablePropertyValues) {
        mpvs = (MutablePropertyValues) pvs;
        //Judge whether it has been converted
        if (mpvs.isConverted()) {
            // Shortcut: use the pre-converted values as-is.
            try {
                bw.setPropertyValues(mpvs);
                return;
            }
            catch (BeansException ex) {
                throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Error setting property values", ex);
            }
        }
        //Get a list of attribute values
        // result = {[email protected]}  size = 1
        // 0 = {[email protected]} "bean property 'userService'"
        //  name = "userService"
        //  value = {[email protected]} "<userService>"
        //  optional = false
        //  converted = false
        //  convertedValue = null
        //  conversionNecessary = null
        //  resolvedTokens = null
        //  source = null
        //  attributes = {[email protected]}  size = 0
        original = mpvs.getPropertyValueList();
    }
    else {
        original = Arrays.asList(pvs.getPropertyValues());
    }

    TypeConverter converter = getCustomTypeConverter();
    if (converter == null) {
        converter = bw;
    }
    BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);

    // Create a deep copy, resolving any references for values.
    List<PropertyValue> deepCopy = new ArrayList<>(original.size());
    boolean resolveNecessary = false;
    for (PropertyValue pv : original) {
        if (pv.isConverted()) {
            deepCopy.add(pv);
        }
        else {
            String propertyName = pv.getName();
            Object originalValue = pv.getValue();
            if (originalValue == AutowiredPropertyMarker.INSTANCE) {
                Method writeMethod = bw.getPropertyDescriptor(propertyName).getWriteMethod();
                if (writeMethod == null) {
                    throw new IllegalArgumentException("Autowire marker for property without write method: " + pv);
                }
                originalValue = new DependencyDescriptor(new MethodParameter(writeMethod, 0), true);
            }
            //Attribute value instantiation
            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);
            }
            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();
    }
    
    //The part without Chinese Notes in the middle can be ignored Not the main logic

    try {
        //Set attribute value
        bw.setPropertyValues(new MutablePropertyValues(deepCopy));
    }
    catch (BeansException ex) {
        throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Error setting property values", ex);
    }
}

This part does the following:

  • Traversed all attribute values (for)
  • Instantiate the bean required for the attribute value and get (resolvevalueifnecessary)
  • Convertforproperty
  • Populate properties (setpropertyvalues)

Set property (setValue)

org.springframework.beans.BeanWrapperImpl.BeanPropertyHandler#setValue

public void setValue(@Nullable Object value) throws Exception {
    //Get the object of the setter method through reflection
    //Output here: public void com zhisan. spring. controller. UserController. setUserService(com.zhisan.spring.service.UserService)
    Method writeMethod = (this.pd instanceof GenericTypeAwarePropertyDescriptor ?
                          ((GenericTypeAwarePropertyDescriptor) this.pd).getWriteMethodForActualAccess() :
                          this.pd.getWriteMethod());
    //Set accessibility
    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 {
        //Set accessibility方法
        ReflectionUtils.makeAccessible(writeMethod);
        //Execution method
        //Value = userservice object
        writeMethod.invoke(getWrappedInstance(), value);
    }
}

Here is the real logic method to fill in attributes, and the principle is also based onreflex

  • Get setter method through reflection
  • Set the processed parameters to the property by executing the setter method.

So far, the completion isFill propertiesThis step, the next step is to complete the last step in the bean life cycleinitialization

Initialize bean (initializebean)

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
            invokeAwareMethods(beanName, bean);
            return null;
        }, getAccessControlContext());
    }
    else {
        //Call aware method
        invokeAwareMethods(beanName, bean);
    }

    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    try {
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
            (mbd != null ? mbd.getResourceDescription() : null),
            beanName, "Invocation of init method failed", ex);
    }
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    return wrappedBean;
    }

In addition to callinginvokeInitMethodsThis method is the main logic. Other parts can be ignored for the time being. Continue readinginvokeInitMethodsmethod.

Invokeinitialmethods

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeAwareMethods

protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
    throws Throwable {

    //Initialize bean
    boolean isInitializingBean = (bean instanceof InitializingBean);
    if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
        if (logger.isTraceEnabled()) {
            logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
        }
        if (System.getSecurityManager() != null) {
            try {
                AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
                    ((InitializingBean) bean).afterPropertiesSet();
                    return null;
                }, getAccessControlContext());
            }
            catch (PrivilegedActionException pae) {
                throw pae.getException();
            }
        }
        else {
            ((InitializingBean) bean).afterPropertiesSet();
        }
    }

    if (mbd != null && bean.getClass() != NullBean.class) {
        String initMethodName = mbd.getInitMethodName();
        if (StringUtils.hasLength(initMethodName) &&
            !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
            !mbd.isExternallyManagedInitMethod(initMethodName)) {
            //Call custom initialization method
            //Determine whether the bean contains initialization methods
            invokeCustomInitMethod(beanName, bean, mbd);
        }
    }
}

The main logic of this part is to judge whether the object needs to execute the initialization method. The specific logic is ininvokeCustomInitMethodIn this method.

Call the invokecustominitmethod

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeCustomInitMethod

  • Gets the initialization method in the bean definition.
  • Get initialization method through reflection
  • Execute initialization method

This is the logic of this method, so I won’t analyze the source code in detail.

last

At this point, all processes of the bean life cycle are completed, and the length of the startup process is over!

In the next article, we will continue to study the design related to these interfaces.