面试官:了解Bean的生命周期吗?说说Spring框架中Bean的生命周期

简介: 云栖号资讯:【点击查看更多行业资讯】在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! 前言 面试过程中被问到Bean的生命周期是很正常的,相信很多人都能答上来,但是放到spring框架里面,难度就提升了很多,今天就来讲一讲Spring框架中Bean的声明周期。

云栖号资讯:【点击查看更多行业资讯
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!


前言

面试过程中被问到Bean的生命周期是很正常的,相信很多人都能答上来,但是放到spring框架里面,难度就提升了很多,今天就来讲一讲Spring框架中Bean的声明周期。

一、Bean的生命周期

关于Bean的生命周期,如果我们不谈这个Spring的话,实际上很多人都会想到New,通过 New 对象的形式来实现对 Bean的实例化操作,而在我们不再使用 Bean 了之后,这时候我们的 Java 就会对这个指定的 Bean 来进行垃圾回收了。

但是对于Spring来说,Bean的生命周期可能就比较让人头疼了,毕竟 Spring 这么复杂,而且里面的对 Bean 管理的非常的有逻辑了,每一层都有每一层的步骤。

如果现在我们去百度上面去搜索所有的关于Spring的Bean的生命周期,很多人会把这个解释出来

在IoC容器启动之后,并不会马上就实例化相应的bean,此时容器仅仅拥有所有对象的BeanDefinition(BeanDefinition:是容器依赖某些工具加载的XML配置信息进行解析和分析,并将分析后的信息编组为相应的BeanDefinition)。只有当getBean()调用时才是有可能触发Bean实例化阶段的活动。

而有一些内容就不会说解释的很透彻,比如说为什么说只有当 getBean() 调用的时候才有可能触发Bean的实例化。

二、生命周期流程图

简化版图解

1

而这图解中,把 Spring 中 Bean 的生命周期分成了好几个步骤,分别是:

通过构造方法实例化 Bean 对象。

通过 setter 方法设置对象的属性。

通过Aware,也就是他的子类BeanNameAware,调用Bean的setBeanName()方法传递Bean的ID(XML里面注册的ID),setBeanName方法是在bean初始化时调用的,通过这个方法可以得到BeanFactory和 Bean 在 XML 里面注册的ID。

如果说 Bean 实现了 BeanFactoryAware,那么工厂调用setBeanFactory(BeanFactory var1) 传入的参数也是自身。把 Bean 实例传递给 BeanPostProcessor 中的 postProcessBeforeInitialization 前置方法。

完成 Bean 的初始化把 Bean 实例传递给 BeanPostProcessor 中的 postProcessAfterInitialization 后置方法。

此时 Bean 已经能够正常时候,在最后的时候调用 DisposableBean 中的 destroy 方法进行销毁处理。

如果面试官在面试的时候问到这个问题的时候,你从图解开始入手,然后把这些都说给他之后,那么相对应的,这现在这些答案,如果不继续的深挖内容,可能已经就足够了。

而接下来还要从根本上来论证上面所写的内容。

而我们对这详细的可能有时候难以记忆,可能还是理解不深,而我们可以从四到五个方面来记忆:

  • 构造实例化
  • 属性赋值
  • 完成初始化
  • (前后处理)
  • 使用后销毁

而从这五个方面来记忆,或许就能把这个图扩展开,从而言简意赅的回答面试官的问题。

代码验证

package com.yld.bean;  import org.springframework.beans.factory.BeanNameAware;  public class Person implements BeanNameAware {      private String name;      /**      * 实现类上的override方法      * @param s      */     @Override     public void setBeanName(String s) {         System.out.println("调用BeanNameAware中的setName赋值");     }      public Person() {     }      /**      * 属性赋值      * @param name      */     public void setName(String name) {         System.out.println("设置对象属性setName()..");         this.name = name;     }      /**      * Bean初始化      */     public void initBeanPerson() {         System.out.println("初始化Bean");     }      /**      * Bean方法使用:说话      */     public void speak() {         System.out.println("使用Bean的Speak方法");     }      /**      * 销毁Bean      */     public void destroyBeanPerson() {         System.out.println("销毁Bean");     }   }

Main方法

public static void main(String[] args) {         ClassPathXmlApplicationContext pathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");         Person person = (Person)pathXmlApplicationContext.getBean("person");         person.speak();         pathXmlApplicationContext.close();     } 

运行结果展示

D:\develop\JDK8\jdk1.8.0_181\bin\java.exe "-javaagent:D:\develop\IDEA\IntelliJ IDEA 2018.1.8\lib\idea_rt.jar=63906:D:\develop\IDEA\IntelliJ IDEA 2018.1.8\bin" -Dfile.encoding=UTF-8 -classpath D:\develop\JDK8\jdk1.8.0_181\jre\lib\charsets.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\deploy.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\dnsns.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\jaccess.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\localedata.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\nashorn.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\sunec.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\zipfs.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\javaws.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\jce.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\jfr.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\jfxswt.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\jsse.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\management-agent.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\plugin.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\resources.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\rt.jar;D:\develop\IDEAProject\KaiYuan\target\classes;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-starter\2.1.8.RELEASE\spring-boot-starter-2.1.8.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot\2.1.8.RELEASE\spring-boot-2.1.8.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-context\5.1.9.RELEASE\spring-context-5.1.9.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-aop\5.1.9.RELEASE\spring-aop-5.1.9.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-beans\5.1.9.RELEASE\spring-beans-5.1.9.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-expression\5.1.9.RELEASE\spring-expression-5.1.9.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\2.1.8.RELEASE\spring-boot-autoconfigure-2.1.8.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-starter-logging\2.1.8.RELEASE\spring-boot-starter-logging-2.1.8.RELEASE.jar;C:\Users\Administrator\.m2\repository\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;C:\Users\Administrator\.m2\repository\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;C:\Users\Administrator\.m2\repository\org\apache\logging\log4j\log4j-to-slf4j\2.11.2\log4j-to-slf4j-2.11.2.jar;C:\Users\Administrator\.m2\repository\org\apache\logging\log4j\log4j-api\2.11.2\log4j-api-2.11.2.jar;C:\Users\Administrator\.m2\repository\org\slf4j\jul-to-slf4j\1.7.28\jul-to-slf4j-1.7.28.jar;C:\Users\Administrator\.m2\repository\javax\annotation\javax.annotation-api\1.3.2\javax.annotation-api-1.3.2.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-core\5.1.9.RELEASE\spring-core-5.1.9.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-jcl\5.1.9.RELEASE\spring-jcl-5.1.9.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\yaml\snakeyaml\1.23\snakeyaml-1.23.jar;C:\Users\Administrator\.m2\repository\org\slf4j\slf4j-api\1.7.28\slf4j-api-1.7.28.jar com.yld.bean.Test 16:54:58.817 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@123772c4 16:54:59.074 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 1 bean definitions from class path resource [applicationContext.xml] 16:54:59.121 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'person'  设置对象属性setName()..  调用BeanNameAware中的setName赋值  初始化Bean  使用Bean的Speak方法  16:54:59.232 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Closing org.springframework.context.support.ClassPathXmlApplicationContext@123772c4, started on Sun Jun 07 16:54:58 CST 2020  销毁Bean  Process finished with exit code 0 

和大家预想的是不是一样的呢? 在用案例回答面试官之后,我们最好还是要研究一下源码的部分,毕竟研究清楚了,会理解的更深刻不是么?

InstantiationAwareBeanPostProcessor

这个类是继承的 BeanPostProcessor 而这个类的作用是什么呢?源码注释解释的是这样子的:

方法一:

@Nullable     default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {         return null;     } 

应用这个Bean处理器在目标Bean实例化之前。返回的bean对象可能是一个代理bean的使用而不是目标,

也就是说

postProcessBeforeInstantiation在bean实例化之前调用的,这是不是也是我们在面试中另外的一个面试点 AOP 的使用呢?到时候面试官让你举例子的时候,你直接用这个 Spring 里面的源码给他解释,分分钟让面试官对你刮目想看呀有木有。

方法二:

可以看到该方法在属性赋值方法内,但是在真正执行赋值操作之前。其返回值为boolean。

default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {         return true;     } 

大家是不是还可以这么理解,如果返回值为false的话,那么就出现了赋值失败,也就是间接阻断赋值了。

而初始化的类同样的 BeanPostProcessor

方法一:

任何Bean之前初始化回调如初始化Bean的属性设置后

@Nullable     default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {         return bean;     } 

方法二:

应用这个Bean后置处理程序给定新的Bean实例,任何Bean初始化后回调(如初始化Bean的属性设置后{@code}或一个自定义的init方法)。bean已经填充属性值。返回的bean实例可能是原始的包装器。

 @Nullable     default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {         return bean;     }

同样注释翻译出来的意思也是很明确的,这也是博主为什么喜欢自己下载个插件去看注释,毕竟源码这个东西如果看别人理解的和自己理解的,有时候差距也是很大的。

【云栖号在线课堂】每天都有产品技术专家分享!
课程地址:https://yqh.aliyun.com/live

立即加入社群,与专家面对面,及时了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK

原文发布时间:2020-06-30
本文作者:Java架构师小马哥
本文来自:“掘金”,了解相关信息可以关注“掘金”

相关文章
|
9天前
|
存储 安全 Java
事件的力量:探索Spring框架中的事件处理机制
事件的力量:探索Spring框架中的事件处理机制
26 0
|
18天前
|
缓存 Java Spring
Spring 框架中 Bean 的生命周期
Spring 框架中 Bean 的生命周期
30 1
|
1月前
|
XML Java 开发者
Spring Boot中的bean注入方式和原理
Spring Boot中的bean注入方式和原理
45 0
|
1月前
|
开发框架 安全 Java
Spring 框架:企业级应用开发的强大工具
在当今数字化时代,企业级应用开发的需求日益增长。为了满足这一需求,开发者们需要一款功能强大、易于使用的开发框架。Spring 框架作为 Java 领域的领先者,为企业级应用开发提供了全面的解决方案。本文将深入探讨 Spring 框架的各个方面,包括其历史、核心模块、优势以及应用场景。
23 0
|
1月前
|
XML 缓存 Java
Spring源码之 Bean 的循环依赖
循环依赖是 Spring 中经典问题之一,那么到底什么是循环依赖?简单说就是对象之间相互引用, 如下图所示: 代码层面上很好理解,在 bean 创建过程中 class A 和 class B 又经历了怎样的过程呢? 可以看出形成了一个闭环,如果想解决这个问题,那么在属性填充时要保证不二次创建 A对象 的步骤,也就是必须保证从容器中能够直接获取到 B。 一、复现循环依赖问题 Spring 中默认允许循环依赖的存在,但在 Spring Boot 2.6.x 版本开始默认禁用了循环依赖 1. 基于xml复现循环依赖 定义实体 Bean java复制代码public class A {
|
1月前
|
人工智能 JSON 前端开发
【Spring boot实战】Springboot+对话ai模型整体框架+高并发线程机制处理优化+提示词工程效果展示(按照框架自己修改可对接市面上百分之99的模型)
【Spring boot实战】Springboot+对话ai模型整体框架+高并发线程机制处理优化+提示词工程效果展示(按照框架自己修改可对接市面上百分之99的模型)
|
3天前
|
Java 数据库连接 开发者
浅谈Spring的Bean生命周期
浅谈Spring的Bean生命周期
10 1
|
7天前
|
XML Java 数据格式
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
18 0
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
|
8天前
|
机器学习/深度学习 分布式计算 BI
Flink实时流处理框架原理与应用:面试经验与必备知识点解析
【4月更文挑战第9天】本文详尽探讨了Flink实时流处理框架的原理,包括运行时架构、数据流模型、状态管理和容错机制、资源调度与优化以及与外部系统的集成。此外,还介绍了Flink在实时数据管道、分析、数仓与BI、机器学习等领域的应用实践。同时,文章提供了面试经验与常见问题解析,如Flink与其他系统的对比、实际项目挑战及解决方案,并展望了Flink的未来发展趋势。附带Java DataStream API代码样例,为学习和面试准备提供了实用素材。
28 0
|
17天前
|
开发框架 安全 Java
探索 Spring 框架:企业级应用开发的强大工具
探索 Spring 框架:企业级应用开发的强大工具
18 1

热门文章

最新文章