面试常见问题丨深入解析Spring中Bean的整个初始化过程

原创 千锋侯哥

前言

在面试过程中,经常有小伙伴会被问到Spring中Bean的生命周期,如果对Spring不了解可能对此类问题难以下手,就算通过百度查询到答案,也可能因为不理解而难以记忆,过段时间又忘记了,那么今天千锋侯哥就带小伙伴深入的解析下Spring中Bean的整个初始化过程。

IOC容器的初始化过程

我们都知道,IOC容器初始化时会进行各种Bean的初始化(单例非懒加载),因此在了解Bean的生命周期之前,我们先来看一下IOC容器的整个初始化过程。

我们先整体看下流程图,做到心中有数,理解无误。

面试常见问题丨深入解析Spring中Bean的整个初始化过程_第1张图片

2.1 开始初始化IOC容器

初始化IOC容器 - 基于常规的注解式容器(AnnotationConfig ApplicationContext

//1、初始化IOC容器
AnnotationConfigApplicationContext applicationContext 
    = new AnnotationConfigApplicationContext(AppConfiguration.class);

AppConfiguration为自定义的一个主配置类,代码如下:

//主配置类
@Configuration
//扫描包路径
@ComponentScan("com.qf")
public class AppConfiguration {
 
}

当前实际开发过程中,IOC容器肯定是随着Web服务器启动而启动的。下图是AnnotationConfigApplicationContext容器的其中一个构造方法(基于主配置类的容器创建)

面试常见问题丨深入解析Spring中Bean的整个初始化过程_第2张图片

2.2 BeanFactory的创建

面试常见问题丨深入解析Spring中Bean的整个初始化过程_第3张图片

SpringIOC容器初始化时,会在ApplicationContext内部创建一个BeanFactory对象。

代码在AnnotationConfigApplicationContext的父类GenericApplicationContext中

 public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
    private final DefaultListableBeanFactory beanFactory;
    ......
     /**
      * 创建一个BeanFacotry对象,实现类为DefaultListableBeanFactory
      */
     public GenericApplicationContext() {
             this.beanFactory = new DefaultListableBeanFactory();
     }
    ......
}  

再来看下DefaultListableBeanFactory中的核心集合部分,这些集合主要保存Bean的描述信息(BeanDefinition)。当然这个时候,这些集合都是空的。

面试常见问题丨深入解析Spring中Bean的整个初始化过程_第4张图片

2.3 主配置类的加载

面试常见问题丨深入解析Spring中Bean的整个初始化过程_第5张图片

加载的代码较为复杂,这里就不给出了,小伙伴理解为主配置被作为普通的Bean对象,放入刚创建好的BeanFactory里就可以了,此时只是注册了Bean的信息,还未初始化为对象。

@Override
public void register(Class... componentClasses) {
    Assert.notEmpty(componentClasses, "At least one component class must be specified");
    StartupStep registerComponentClass = this.getApplicationStartup().start("spring.context.component-classes.register")
            .tag("classes", () -> Arrays.toString(componentClasses));
    this.reader.register(componentClasses);
    registerComponentClass.end();
} 

2.4 BeanFactory的后置处理

面试常见问题丨深入解析Spring中Bean的整个初始化过程_第6张图片

这一步至关重要,其中有个BeanFactory的后置处理器 - ConfigurationClass PostProcessor,从名称可以看出,该处理器是为了初始化配置类而生的。上一步中注册到BeanFactory的主配置类,将会被这个后置处理器初始化为Bean对象,并且处理配置类上的@ComponentScan("com.qf")注解,进行其他组件的扫描,扫描的部分核心代码如下:

//基于包路径,扫描路径下的所有Bean,并且返回这些Bean的基本信息(BeanDefinition)集合
protected Set doScan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    Set beanDefinitions = new LinkedHashSet<>();
    //循环需要扫描的包
    for (String basePackage : basePackages) {
        //依次解析指定包下的各种Bean组件,返回BeanDefintion集合
        Set candidates = findCandidateComponents(basePackage);
 
        //再次完善各个Bean组件的基本信息
        for (BeanDefinition candidate : candidates) {
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
 
            //设置Bean的作用域类型
            candidate.setScope(scopeMetadata.getScopeName());
 
            //获取Bean的名称
            String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
 
            if (candidate instanceof AbstractBeanDefinition) {
                postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
            }
 
            if (candidate instanceof AnnotatedBeanDefinition) {
                AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
            }
 
            //检查当前bean是否已经注册过,如果没有注册,则继续
            if (checkCandidate(beanName, candidate)) {
                //将BeanDefinition对象包装成BeanDefinitionHolder对象
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                definitionHolder =
                        AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                //放入集合
                beanDefinitions.add(definitionHolder);
                //并且将当前BeanDefinition对象注册到IOC容器中
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
} 

2.5 BeanFactory中其他Bean的初始化

面试常见问题丨深入解析Spring中Bean的整个初始化过程_第7张图片

上一步中,已经通过主配置类的扫描将各种业务Bean注册到BeanFactory了,接下来就是执行各种Bean的初始化流程了,这个过程也就是Bean的生命周期执行过程,我们稍后再来解析。

2.6 BeanFactory初始化完成

Bean初始化完成后,整个BeanFactory也就初始化完成了,IOC容器也就完成,相关数据集合也填充完毕,等待后续的业务执行。

面试常见问题丨深入解析Spring中Bean的整个初始化过程_第8张图片

三、Bean的初始化过程(生命周期)

接下来我们重点来看下Bean是如何初始化的,执行实际也就是上一节的第5步。当然,这个过程主要是初始化那些单例并且非懒加载的Bean,懒加载的Bean和原型的Bean是后续需要用到该Bean时才进行初始化(比如手动调用getBean等),此时才并未进行完整的初始化,暂且不讨论。

3.1 Bean初始化的流程图

老规矩,先看整体流程(红框部分)

面试常见问题丨深入解析Spring中Bean的整个初始化过程_第9张图片

因为后续的内容会用到,所以这里要跟大家区分两个概念:

  • Bean的创建

  • Bean的初始化

所谓的Bean的创建,其实就是指Bean对象通过构造方法,在堆内存中被创建出来的过程,此时站在Java的角度,该对象已经完成了初始化,但是内部的属性只拥有默认值,没有任何业务意义。

所谓的Bean的初始化,是站在Spring的角度,还需要对已经创建好的Bean进行后续的一些操作,比如依赖注入之类的,这个过程会给Bean进行一些业务操作,成为一个真正的"成品",可以即拿即用。而这个过程正是Bean的生命周期过程。

3.2 单例Bean初始化入口

接下来我们看下初始化Bean的核心方法,从名字就可以看出,完成BeanFactory的初始化,注释意为,初始化所有的非懒加载单例Bean

其中的核心调用方法,初始化所有单例Bean

@Override
public void preInstantiateSingletons() throws BeansException {
    ....
    //获取前面注册过的所有Bean的名称,转储到一个新集合
    List beanNames = new ArrayList<>(this.beanDefinitionNames);
    //循环所有Bean的名称,依次处理
    for (String beanName : beanNames) {
        //根据Bean的名称获取Bean的基本信息(BeanDefinition)
        RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
        //判断是否为非抽象、单例、非懒加载
        if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
            //判断是否为FactoryBean对象,
            if (isFactoryBean(beanName)) {
                //处理FactoryBean的初始化
            }
            else {
                //普通Bean的初始化过程.....
                getBean(beanName);
            }
        }
    }
    // 触发部分Bean的初始化的回调方法
    //.....
}

3.3 单例Bean的创建(基于反射)

getBean方法,是通过Bean的名称获取Bean对象,该方法中就会判断IOC容器中是否存在Bean对象,如果不存在就会执行初始化流程,核心方法如下:

面试常见问题丨深入解析Spring中Bean的整个初始化过程_第10张图片

//该方法在getBean方法中被调用
protected  T doGetBean(
        String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly)
        throws BeansException {
    //获得bean的名称
    String beanName = transformedBeanName(name);
    Object beanInstance;
    //从缓存中获取单例Bean的缓存(为了解决循环依赖的设计)
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
        //从缓存已经获取缓存的Bean对象的处理逻辑
        ......
    } else {
        //缓存中没有找到单例Bean对象的处理逻辑
        if (mbd.isSingleton()) {
            //单例Bean的初始化逻辑【核心】
            sharedInstance = getSingleton(beanName, () -> {
                try {
                    //创建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;
                }
            });
            beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
        } else if (mbd.isPrototype()) {
            //原型模式的处理逻辑,IOC容器初始化时并不会触发原型模式的初始化
            .....
        } else {
            //其他作用域类型的处理逻辑
            .....
        }
    }
    return adaptBeanInstance(name, beanInstance, requiredType);
}

//createBean方法中的核心方法doCreateBean
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
        throws BeanCreationException {
    BeanWrapper instanceWrapper = null;
    if (instanceWrapper == null) {
        //核心 - 反射创建Bean对象,并且封装到BeanWrapper对象(Bean的包装器)中
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
 
    //获得当前Bean对象
    Object bean = instanceWrapper.getWrappedInstance();
    Class beanType = instanceWrapper.getWrappedClass();
    .....
    Object exposedObject = bean;
    try {
        //核心 - 给Bean的属性进行依赖注入,方法翻译过来叫填充Bean
        populateBean(beanName, mbd, instanceWrapper);
        //核心 - 然后执行Bean的后续初始化过程,具体细节见下方
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    }
    .....
    //返回Bean对象    
    return exposedObject;
}

3.4 单例Bean的初始化

上一步已经展示了,Spring合适创建了Bean对象,以及依赖注入和初始化过程。这里重点来看下Bean的后续初始化过程,核心代码如下:

面试常见问题丨深入解析Spring中Bean的整个初始化过程_第11张图片

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
  
    //核心 - 调用Bean的各种XxxxAware接口的方法,进行相关属性填充
    invokeAwareMethods(beanName, bean);
 
    //核心 - 调用BeanPostProcessor的前置处理方法
    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    //核心 - 调用Bean的初始化方法(init-method或者@PostConstruct注解标记的方法)
    try {
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
                (mbd != null ? mbd.getResourceDescription() : null),
                beanName, "Invocation of init method failed", ex);
    }
    //核心 - 调用BeanPostProcessor的后置处理方法
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    return wrappedBean;
}

四、总结

因为Spring拥有很庞大的体系结构,没办法介绍的面面俱到,因此摘了部分核心代码,配合流程图讲解,基本把整个Bean的初始化乃至整个IOC容器的初始化过程,简述了出来。如果小伙伴想要更加深入的了解Spring源码,可以配合debug,走一遍IOC初始化的流程。

最后再总结下整个Bean的整个生命周期过程

  • 1、Spring通过反射创建Bean对象

  • 2、完成当前Bean的依赖注入(成员变量填充);

  • 3、如果Bean有实现Aware接口,则调用各种Aware接口的方法设置属性;

  • 4、执行BeanPostProcessor前置方法(通常是一个集合,形成一个调用链,依次执行);

  • 5、完成开发者自定义的初始化方法(init-method或者@PostConstruct注解标记的方法);

  • 6、执行BeanPostProcessor后置方法(通常是一个集合,形成一个调用链,依次执行);

  • 7、完成初始化,将Bean放入BeanFactory集合中(核心是一个Map集合);

  • 8、Bean对象随着BeanFactory关闭而销毁,执行开发者自定义的销毁方法(destory-method或者@PreDestory注解标记的方法)。面试常见问题丨深入解析Spring中Bean的整个初始化过程_第12张图片

你可能感兴趣的:(马剑威老师-Java专栏,面试,spring,java)