Spring Bean生命周期七大阶段-Java八股面试(七)

系列文章目录

第一章 ArrayList-Java八股面试(一)

第二章 HashMap-Java八股面试(二)

第三章 单例模式-Java八股面试(三)

第四章 线程池和Volatile关键字-Java八股面试(四)

第五章 ConcurrentHashMap-Java八股面试(五)

第六章 spring之refresh流程-Java八股面试(六)


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

提示:这里可以添加本文要记录的大概内容:

例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。


提示:以下是本篇文章正文内容,下面案例可供参考

一、Spring Bean

在对于Spring的所有解读中,Bean的生命周期都可谓是重中之重,甚至还有人称Spring就是个管理Bean的容器。Bean的生命周期之所以这么重要,被反复提及,是因为Spring的核心能力,比如对象创建(IOC)、属性注入(DI)、初始化方法的调用、代理对象的生成(AOP)等功能的实现,都是在bean的生命周期中完成的。
对于普通的 Java 对象,当 new 的时候创建对象,然后该对象就能够使用了。一旦该对象不再被使用,则由 Java 自动进行垃圾回收。
而 Spring 中的对象是 bean,bean 和普通的 Java 对象没啥大的区别,只不过 Spring 不再自己去 new 对象了,而是由 IoC 容器去帮助我们实例化对象并且管理它,我们需要哪个对象,去问 IoC 容器要即可。IoC 其实就是解决对象之间的耦合问题,Spring Bean 的生命周期完全由容器控制。

对于Bean的创建可分为7个阶段。
在这里插入图片描述

阶段4中的scope可分为以下几种:

  • singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的。
  • prototype : 每次请求都会创建一个新的 bean 实例。
  • request : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP request 内有效。
  • session : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP session 内有效。
  • global-session: 全局 session 作用域,仅仅在基于 Portlet 的 web 应用中才有意义,Spring5已经没有了。Portlet 是能够生成语义代码(例如:HTML)片段的小型 Java Web 插件。它们基于 portlet 容器,可以像servlet 一样处理 HTTP 请求。但是,与 servlet 不同,每个 portlet 都有不同的会话。

需要注意的是singleton并不代表只有一个Bean,因为在程序中可能存在多个容器,自然就可能有多个Bean,所以还是视情况而论。

1.1 处理名称,检查缓存

在这里插入图片描述

需要注意的是三级缓存是在后续解决循环依赖时所需要的,下一篇文章会更新如何解决循环依赖。

1.2 检查父工厂

在这里插入图片描述

1.3 检查DependsOn

如果有2个类A和B,且B要监听A,则希望B在A之前加载到容器中。
但是Spring默认是根据文件夹中类名的顺序加载,例如字母A在B之前,则会先加载A。

@DependsOn注解可以设置Bean直接的依赖关系,被依赖的会先创建加载到Spring容器中。

下面举例:有一个老师类:Teacher,一个迟到学生类:LateStudent。

老师要检查哪些学生迟到,则老师得先到学校,在教室门口等着迟到的学生。

对于Spring容器而言,得先加载Teacher,后加载LateStudent,这样Teacher才能监听所有的LateStudent。

1.4 按Scope创建Bean

在这里插入图片描述

1.5 创建Bean

在这里插入图片描述

Bean 创建流程入口AbstractApplicationContext#refresh() 方法的 finishBeanFactoryInitialization(beanFactory) 中,refresh()方法的介绍在上一章中,感兴趣的可以去看看。

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);
}
...
Object exposedObject = bean;
try { 

// 属性赋值阶段
this.populateBean(beanName, mbd, instanceWrapper);
// 初始化阶段
exposedObject = this.initializeBean(beanName, exposedObject, mbd);
} catch (Throwable var18) { 

...
}
...
}

总流程图如下:
在这里插入图片描述

1.5.1 创建Bean实例

对象的创建是bean生命周期的第一步,毕竟要先有1才能有0嘛。创建对象的方式有很多,比如 new、反射、clone等等,Spring是怎么创建对象的呢?绝大多数情况下,Spring是通过反射来创建对象的,不过如果我们提供了Supplier或者工厂方法,Spring也会直接使用我们提供的创建方式。

我们从源码出发,看一下Spring是如何选择创建方式的:

// 源码位于 AbstractAutowireCapableBeanFactory.javaprotected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
   // 再次解析BeanDefinition的class,确保class已经被解析
   Class<?> beanClass = resolveBeanClass(mbd, beanName);

   // 1: 如果提供了Supplier,通过Supplier产生对象
   Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
   if (instanceSupplier != null) {
      return obtainFromSupplier(instanceSupplier, beanName);
   }

   // 2: 如果有工厂方法,使用工厂方法产生对象// 在@Configration配置@Bean的方法,也会被解析为FactoryMethodif (mbd.getFactoryMethodName() != null) {
      return instantiateUsingFactoryMethod(beanName, mbd, args);
   }
   //...省略部分代码// 3: 推断构造方法// 3.1 执行后置处理器,获取候选构造方法
   Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
   // 3.2 需要自动注入的情况if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
         mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
      return autowireConstructor(beanName, mbd, ctors, args);
   }

   // 3.3 默认使用没有参数的构造方法return instantiateBean(beanName, mbd);
}

具体逻辑是:

先判断是否提供了Supplier,如果提供,则通过Supplier产生对象。

再判断是否提供工厂方法,如果提供,则使用工厂方法产生对象。

如果都没提供,需要进行构造方法的推断,逻辑为:

如果仅有一个构造方法,会直接使用该构造方法(如果构造方法有参数,会自动注入依赖参数)

如果有多个构造参数,会判断有没有加了@Autowired注解的构造参数:

如果没有,Spring默认选择无参构造方法;

如果有,且有@Autowired(required=true)的构造方法,就会选择该构造方法;

如果有,但是没有@Autowired(required=true)的构造方法,Spring会从所有加了@Autowired的构造方法中,根据构造器参数个数、类型匹配程度等综合打分,选择一个匹配参数最多,类型最准确的构造方法。

简而言之,我们只需要简单知道Spring会选择一个构造方法,然后通过反射创建出对象即可。

在这里插入图片描述

1.5.2 依赖注入

1.后处理器拓展
在依赖注入之前,MergedBeanDefinitionPostProcessor类型的后置处理器,可以对bean对应的BeanDefinition进行修改,本阶段是Spring提供的一个拓展点。Spring自身也充分利用该拓展点,做了很多初始化操作(并没有修改BeanDefinition),比如查找标注了@Autowired、 @Resource、@PostConstruct、@PreDestory 的属性和方法,方便后续进行属性注入和初始化回调。当然,我们也可以自定义实现,用来修改BeanDefinition信息或者我们需要的初始化操作。

2.三级缓存
经过后处理器的拓展之后,早期bean对象(注意还没有完成初始化,只是实例化)提前放入到三级缓存singletonFactories中,为循环依赖做支持。在后续进行属性填充时,如果发生循环依赖,可以从三级缓存中通过getObject()获取该bean,完成循环依赖场景下的自动注入。

booleanearlySingletonExposure= (mbd.isSingleton() && this.allowCircularReferences &&
      isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
   if (logger.isTraceEnabled()) {
      logger.trace("Eagerly caching bean '" + beanName +
            "' to allow for resolving potential circular references");
   }
   // 做循环依赖的支持 将早期实例化bean的ObjectFactory,添加到单例工厂(三级缓存)中
   addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

3.依赖注入
最后完成Spring的核心功能之一:依赖注入,包括自动注入、@Autowired注入、@Resource注入等。Spring会根据bean的注入模型(默认不自动注入),选择根据名称自动注入还是根据类型自动注入。然后调用InstantiationAwareBeanPostProcessor#postProcessProperties()完成@Autowired和@Resource的属性注入。

protectedvoidpopulateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
   // 省略部分代码// 获取bean的注入类型intresolvedAutowireMode= mbd.getResolvedAutowireMode();
   // 1: 自动注入if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
      MutablePropertyValuesnewPvs=newMutablePropertyValues(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;
   }


   // 2: 调用BeanPostProcessor,完成@Autowired @Resource属性填充
   PropertyDescriptor[] filteredPds = null;
   if (hasInstAwareBpps) {
      if (pvs == null) {
         pvs = mbd.getPropertyValues();
      }
      for (BeanPostProcessor bp : getBeanPostProcessors()) {
         if (bp instanceof InstantiationAwareBeanPostProcessor) {
            InstantiationAwareBeanPostProcessoribp= (InstantiationAwareBeanPostProcessor) bp;

            // 重点: 完成@Autowired @Resource属性填充PropertyValuespvsToUse= ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
            if (pvsToUse == null) {
               if (filteredPds == null) {
                  // 需要注入的属性,会过滤掉Aware接口包含的属性(通过ignoreDependencyInterface添加)
                  filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
               }
               pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
               if (pvsToUse == null) {
                  return;
               }
            }
            pvs = pvsToUse;
         }
      }
   }
  
    // 3: 依赖检查if (needsDepCheck) {
      if (filteredPds == null) {
         filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
      }
      checkDependencies(beanName, mbd, filteredPds, pvs);
   }
   // 4: 将属性应用到bean中if (pvs != null) {
      applyPropertyValues(beanName, mbd, bw, pvs);
   }
}

在这里插入图片描述

1.5.3 初始化Bean

该阶段主要做bean的初始化操作,包括:回调Aware接口、回调初始化方法、生成代理对象等。

invokeAwareMethods():回调BeanNameAware、BeanClassLoaderAware、BeanFactoryAware感知接口。

回调后置处理器的前置方法,其中:

ApplicationContextAwareProcessor: 回调EnvironmentAware、ResourceLoaderAware、ApplicationContextAware、ApplicationEventPublisherAware、MessageSourceAware、EmbeddedValueResolverAware感知接口。

InitDestroyAnnotationBeanPostProcessor:调用了标注了@PostConstruct的方法。

invokeInitMethods()调用初始化方法:
如果bean是InitializingBean的子类, 先调用afterPropertiesSet()。

回调自定义的initMethod,比如通过@Bean(initMethod = “xxx”)指定的初始化方法。

回调后置处理器的后置方法,可能返回代理对象。其中AbstractAutoProxyCreator和 AbstractAdvisingBeanPostProcessor都有可能产生代理对象,比如InfrastructureAdvisorAutoProxyCreator完成了@Transactional代理对象的生成,AsyncAnnotationBeanPostProcessor完成了@Async代理对象的生成。
在初始化完成后,bean会被放到单例池中,正式开始自己的使命:为项目服务,比如接收http请求,进行CRUD等等。后续有使用到该bean的地方,也是直接从单例池中获取,不会再次创建bean(仅单例的哦)。
在这里插入图片描述

1.5.4 登记可销毁Bean

在创建bean的时候,会判断如果bean是DisposableBean、AutoCloseable的子类,或者有 destroy-method等,会注册为可销毁的bean,在容器关闭时,调用对应的方法进行bean的销毁。
在这里插入图片描述

1.6 类型转换

在这里插入图片描述

1.7 销毁Bean

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值