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


    原创 千锋侯哥

    前言

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

    IOC容器的初始化过程

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

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

    2.1 开始初始化IOC容器

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

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

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

    1. //主配置类
    2. @Configuration
    3. //扫描包路径
    4. @ComponentScan("com.qf")
    5. public class AppConfiguration {
    6.  
    7. }

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

    2.2 BeanFactory的创建

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

    代码在AnnotationConfigApplicationContext的父类GenericApplicationContext中

    1.  public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
    2.     private final DefaultListableBeanFactory beanFactory;
    3.     ......
    4.      /**
    5.       * 创建一个BeanFacotry对象,实现类为DefaultListableBeanFactory
    6.       */
    7.      public GenericApplicationContext() {
    8.              this.beanFactory = new DefaultListableBeanFactory();
    9.      }
    10.     ......
    11. }  

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

    2.3 主配置类的加载

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

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

    2.4 BeanFactory的后置处理

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

    1. //基于包路径,扫描路径下的所有Bean,并且返回这些Bean的基本信息(BeanDefinition)集合
    2. protected Set<BeanDefinitionHolderdoScan(String... basePackages) {
    3.     Assert.notEmpty(basePackages, "At least one base package must be specified");
    4.     Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    5.     //循环需要扫描的包
    6.     for (String basePackage : basePackages) {
    7.         //依次解析指定包下的各种Bean组件,返回BeanDefintion集合
    8.         Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
    9.  
    10.         //再次完善各个Bean组件的基本信息
    11.         for (BeanDefinition candidate : candidates) {
    12.             ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
    13.  
    14.             //设置Bean的作用域类型
    15.             candidate.setScope(scopeMetadata.getScopeName());
    16.  
    17.             //获取Bean的名称
    18.             String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
    19.  
    20.             if (candidate instanceof AbstractBeanDefinition) {
    21.                 postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
    22.             }
    23.  
    24.             if (candidate instanceof AnnotatedBeanDefinition) {
    25.                 AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
    26.             }
    27.  
    28.             //检查当前bean是否已经注册过,如果没有注册,则继续
    29.             if (checkCandidate(beanName, candidate)) {
    30.                 //将BeanDefinition对象包装成BeanDefinitionHolder对象
    31.                 BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
    32.                 definitionHolder =
    33.                         AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    34.                 //放入集合
    35.                 beanDefinitions.add(definitionHolder);
    36.                 //并且将当前BeanDefinition对象注册到IOC容器中
    37.                 registerBeanDefinition(definitionHolder, this.registry);
    38.             }
    39.         }
    40.     }
    41.     return beanDefinitions;

    2.5 BeanFactory中其他Bean的初始化

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

    2.6 BeanFactory初始化完成

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

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

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

    3.1 Bean初始化的流程图

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

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

    • Bean的创建

    • Bean的初始化

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

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

    3.2 单例Bean初始化入口

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

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

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

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

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

    1. //该方法在getBean方法中被调用
    2. protected <T> T doGetBean(
    3.         String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
    4.         throws BeansException {
    5.     //获得bean的名称
    6.     String beanName = transformedBeanName(name);
    7.     Object beanInstance;
    8.     //从缓存中获取单例Bean的缓存(为了解决循环依赖的设计)
    9.     Object sharedInstance = getSingleton(beanName);
    10.     if (sharedInstance != null && args == null) {
    11.         //从缓存已经获取缓存的Bean对象的处理逻辑
    12.         ......
    13.     } else {
    14.         //缓存中没有找到单例Bean对象的处理逻辑
    15.         if (mbd.isSingleton()) {
    16.             //单例Bean的初始化逻辑【核心】
    17.             sharedInstance = getSingleton(beanName, () -> {
    18.                 try {
    19.                     //创建Bean对象,底层通过反射实现,具体分析见下方
    20.                     return createBean(beanName, mbd, args);
    21.                 }
    22.                 catch (BeansException ex) {
    23.                     // Explicitly remove instance from singleton cache: It might have been put there
    24.                     // eagerly by the creation process, to allow for circular reference resolution.
    25.                     // Also remove any beans that received a temporary reference to the bean.
    26.                     destroySingleton(beanName);
    27.                     throw ex;
    28.                 }
    29.             });
    30.             beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    31.         } else if (mbd.isPrototype()) {
    32.             //原型模式的处理逻辑,IOC容器初始化时并不会触发原型模式的初始化
    33.             .....
    34.         } else {
    35.             //其他作用域类型的处理逻辑
    36.             .....
    37.         }
    38.     }
    39.     return adaptBeanInstance(name, beanInstance, requiredType);
    40. }
    41. //createBean方法中的核心方法doCreateBean
    42. protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
    43.         throws BeanCreationException {
    44.     BeanWrapper instanceWrapper = null;
    45.     if (instanceWrapper == null) {
    46.         //核心 - 反射创建Bean对象,并且封装到BeanWrapper对象(Bean的包装器)中
    47.         instanceWrapper = createBeanInstance(beanName, mbd, args);
    48.     }
    49.  
    50.     //获得当前Bean对象
    51.     Object bean = instanceWrapper.getWrappedInstance();
    52.     Class<?> beanType = instanceWrapper.getWrappedClass();
    53.     .....
    54.     Object exposedObject = bean;
    55.     try {
    56.         //核心 - 给Bean的属性进行依赖注入,方法翻译过来叫填充Bean
    57.         populateBean(beanName, mbd, instanceWrapper);
    58.         //核心 - 然后执行Bean的后续初始化过程,具体细节见下方
    59.         exposedObject = initializeBean(beanName, exposedObject, mbd);
    60.     }
    61.     .....
    62.     //返回Bean对象    
    63.     return exposedObject;
    64. }

    3.4 单例Bean的初始化

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

    1. protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    2.   
    3.     //核心 - 调用Bean的各种XxxxAware接口的方法,进行相关属性填充
    4.     invokeAwareMethods(beanName, bean);
    5.  
    6.     //核心 - 调用BeanPostProcessor的前置处理方法
    7.     Object wrappedBean = bean;
    8.     if (mbd == null || !mbd.isSynthetic()) {
    9.         wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    10.     }
    11.     //核心 - 调用Bean的初始化方法(init-method或者@PostConstruct注解标记的方法)
    12.     try {
    13.         invokeInitMethods(beanName, wrappedBean, mbd);
    14.     }
    15.     catch (Throwable ex) {
    16.         throw new BeanCreationException(
    17.                 (mbd != null ? mbd.getResourceDescription() : null),
    18.                 beanName, "Invocation of init method failed", ex);
    19.     }
    20.     //核心 - 调用BeanPostProcessor的后置处理方法
    21.     if (mbd == null || !mbd.isSynthetic()) {
    22.         wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    23.     }
    24.     return wrappedBean;
    25. }

    四、总结

    因为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注解标记的方法)。

  • 相关阅读:
    excel中怎么用乘法、加法来替代AND和OR函数
    加密行业焦点:本周五,关注灰度GBTC转型是否有解?
    await 关键字在 async 函数中确实表现出“等待”的效果,那异步的含义呢,异步指整个不影响主进程
    多数据源切换
    Kruise Rollout v0.2.0 版本发布:支持 Gateway API、StatefulSet 分批发布等能力
    文件上传表单的 HTML 页面+PHP
    benders分解算法 逻辑思路整理(加星)
    安卓玩机搞机----不用刷第三方官改固件即可享受“高级设置”的操作 ChiMi安装使用步骤
    字节的面试题到底有多难?大厂为何都注重算法?我们该如何应对?
    阿里云K8s容器Pod中Java进程CPU占比100%排查
  • 原文地址:https://blog.csdn.net/finally_vince/article/details/125563578