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

news2024/10/2 4:24:25

系列文章目录

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

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

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

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

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

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


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

文章目录

  • 系列文章目录
  • 前言
  • 一、Spring Bean
    • 1.1 处理名称,检查缓存
    • 1.2 检查父工厂
    • 1.3 检查DependsOn
    • 1.4 按Scope创建Bean
    • 1.5 创建Bean
      • 1.5.1 创建Bean实例
      • 1.5.2 依赖注入
      • 1.5.3 初始化Bean
      • 1.5.4 登记可销毁Bean
    • 1.6 类型转换
    • 1.7 销毁Bean


前言

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

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


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

一、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

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/397715.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HTTPS是怎么加密数据的?

HTTPS是怎么加密数据的&#xff1f;对安全或密码学基础有了解的同学&#xff0c;应该知道常见的加密手段。一般来说&#xff0c;加密分为对称加密、非对称加密&#xff08;也叫公开密钥加密&#xff09;对称加密对称加密的意思就是&#xff0c;加密数据用的密钥&#xff0c;跟解…

儿童反复感染,是体质差还是免疫缺陷?

原发性免疫缺陷病&#xff08;PIDs&#xff09;它是一组由遗传因素或先天性免疫系统发育不良引起的免疫系统功能障碍综合征&#xff0c;可涉及固有免疫或适应性免疫。在中国&#xff0c;PID的中位发病率为6个月&#xff0c;男孩的发病率通常高于女孩。▼分类目前&#xff0c;国…

LearnOpenGL-光照-2.基础光照

本人刚学OpenGL不久且自学&#xff0c;文中定有代码、术语等错误&#xff0c;欢迎指正 我写的项目地址&#xff1a;https://github.com/liujianjie/LearnOpenGLProject 文章目录基础光照环境光照漫反射光照法向量计算漫反射光照最后一件事镜面光照基础光照 简介 现实世界的光照…

JavaEE初阶---初始进程

系统分配资源的最小单元--进程啥是进程看看进程操作系统如何管理进程PCB中的一些属性pid(进程id)内存指针文件描述符表进程调度并行和并发:进程调度的特性状态:优先级:记账信息:上下文:虚拟地址空间进程间交互我们日常使用计算机,大致可以抽象成如下形式:自上向下依次是 各种软…

易优cms switch 条件判断标签使用方法

【基础用法】 标签&#xff1a;switch 描述&#xff1a;简单条件判断&#xff0c;比if判断标签少些不等于相同功能&#xff0c;视个人习惯而用。 用法&#xff1a; {eyou:switch name$eyou.field.has_children} {eyou:case value1}当前栏目列表的栏目ID有1个下级栏目{/eyo…

跨境电商卖家如何应对拒付、盗卡

跨境电商主要是通过电子商务平台进行交易&#xff0c;在平台上完成支付结算&#xff0c;并通过国际物流将商品送达买方&#xff0c;从而实现跨国零售交易。与传统贸易相比&#xff0c;跨境电商存在交易链条更短、回款周期更快、数据及时透明等优势。商务部数据显示&#xff0c;…

sumifs的交叉 表的例子

比如这样&#xff0c;那么冰箱绿山店的栏位中&#xff0c;SUMIFS($D$3:$D$10,$B$3:$B$10,$F3,$C$3:$C$10,G$2)就是把求和范围&#xff0c;条件1设置为固定列的复合引用&#xff0c;条件2设置为固定行的复合引用即可。

LeetCode 1653. Minimum Deletions to Make String Balanced【字符串,动态规划,枚举】中等

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

shell基本知识

为什么学习和使用Shell编程 什么是Shell shell的起源 shell的功能 shell的分类 如何查看当前系统支持的shell&#xff1f; 如何查看当前系统默认shell&#xff1f; 驼峰语句 shell脚本的基本元素 shell脚本编写规范 shell脚本的执行方式 shell脚本的退出状态 &#xf…

数位dp-- 数字游戏

题目 思路 也是一道比较典型的数位dp的问题&#xff0c;关键的思想跟我上一篇博客很像&#xff0c; 首先把区间值变成[1,Y]-[1,X-1]的值&#xff0c;然后单独计算得到结果。 总的来说就是把这个数的每一位都单独拿出来&#xff0c;然后根据选0-an-1和选**an**两种方案单独计算&…

LeetCode 热题 C++ 538. 把二叉搜索树转换为累加树 543. 二叉树的直径 560. 和为 K 的子数组

538. 把二叉搜索树转换为累加树 给出二叉 搜索 树的根节点&#xff0c;该树的节点值各不相同&#xff0c;请你将其转换为累加树&#xff08;Greater Sum Tree&#xff09;&#xff0c;使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。 提醒一下&#xff0c;二…

Java基础算法题

目录 练习一 : 优化代码 扩展 : CRTL Alt M 自动抽取方法 练习二: 方法1: 方法2: 方法3: Math : 顾名思义&#xff0c;Math类就是用来进行数学计算的&#xff0c;它提供了大量的静态方法来便于我们实现数学计算&#xff1a; 练习三 : 练习四 : 练习五: 练习…

【GO】K8s 管理系统项目34[Docker方式–应用部署]

K8s 管理系统项目[Docker方式–应用部署] 1. 数据库 1.1 创建数据库目录 mkdir -p /data/mysql5.7/1.2 创建容器 docker run --name mysql -itd -h mysql-server -e MYSQL_ROOT_PASSWORDroot -v /data/mysql5.7:/var/lib/mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD123456 …

layui框架实战案例(19):layui-table模块表格综合应用(筛选查询、导入导出、群发短信、一键审核、照片展示、隐私加密)

系列文章目录 layui动态表格翻页和搜索的代码分析layui框架实战案例(3)&#xff1a;layui上传错误请求上传接口出现异常解决方案layui框架实战案例(9)&#xff1a;layPage 静态数据分页组件layui框架实战案例(10)&#xff1a;短信验证码60秒倒计时layui框架实战案例(11)&#…

《实践论》笔记及当下反思(二)

目录 笔记 1、马克思主义所说的绝对真理是什么&#xff1f; 2、客观现实世界的变化运动永远没有完结&#xff0c;人们在实践中对于真理的认识也就永远没有完结 3、改造客观世界&#xff0c;也改造自己的主观世界-——改造自己的认识能力 4、实践、认识、再实践、再认识&…

ARP报文内容详细分析

ARP报文格式如图&#xff1a; 字段1&#xff1a;ARP请求的目的以太网地址&#xff0c;全1时&#xff0c;代表广播地址。 字段2&#xff1a;发送ARP请求的以太网地址。 字段3&#xff1a;以太网帧类型表示后面的数据类型&#xff0c;ARP请求和ARP应答此字段为&#xff1a;0x0806…

SourceTree 重置提交、合并、撤销、回滚

SourceTree重置当前分支到此次提交使用场景&#xff1a;已提交未推送的修改撤销、想把某一次的错误修改全部撤销当前发布代码有bug需要切到上次提交发布版本Git中的HEAD解释# 使用最新一次提交重制暂存区git reset HEAD -- filename# 使用最新一次提交重制暂存区和工作区git re…

SpringBoot——统一功能处理

处理登陆拦截 上一片博客中讲到SpringAOP可以对页面进行拦截&#xff0c;我们可以用SpringAOP实现对登陆的拦截 但是由于拦截需要HttpSession对象&#xff0c;并且之后还需要页面重定向&#xff0c;因此在实际应用中&#xff0c;并不用SpringAOP进行登陆拦截&#xff0c;而是…

HBase---idea操作Hbase数据库并且映射到Hive

idea操作Hbase数据库并且映射到Hive 文章目录idea操作Hbase数据库并且映射到Hiveidea操作Hbase数据库环境准备启动服务创建Maven工程在测试类中编写初始化方法在测试类中编写关闭方法在测试类中编写创建命名空间方法在测试类中编写创建表方法在测试类中编写查看表结构方法在测试…

tmall.product.match.schema.get( 获取匹配产品规则 )

&#xffe5;免费必须用户授权 ISV发布商品前&#xff0c;需要先查找到产品ID&#xff0c;这个接口返回查找产品规则入参规则 公共参数 请求地址: HTTP地址 http://gw.api.taobao.com/router/rest 公共请求参数: 公共响应参数: 点击获取key和secret 请求示例 TaobaoClient …