面试官:了解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架构师小马哥
本文来自:“掘金”,了解相关信息可以关注“掘金”

相关文章
|
13天前
|
安全 Java 开发者
如何在Spring框架中实现横切关注点的集中管理和重用?
【4月更文挑战第30天】如何在Spring框架中实现横切关注点的集中管理和重用?
18 0
|
13天前
|
安全 Java 程序员
Spring框架的核心特性是什么?
【4月更文挑战第30天】Spring 的特性
17 0
|
1天前
|
监控 Java 应用服务中间件
Spring Boot 源码面试知识点
【5月更文挑战第12天】Spring Boot 是一个强大且广泛使用的框架,旨在简化 Spring 应用程序的开发过程。深入了解 Spring Boot 的源码,有助于开发者更好地使用和定制这个框架。以下是一些关键的知识点:
12 6
|
2天前
|
XML Java 数据库连接
Spring框架与Spring Boot的区别和联系
Spring框架与Spring Boot的区别和联系
10 0
|
4天前
|
前端开发 安全 Java
使用Spring框架加速Java开发
使用Spring框架加速Java开发
10 0
|
4天前
|
设计模式 数据采集 监控
Spring日志框架
Spring日志框架
8 0
|
4天前
|
前端开发 Java 应用服务中间件
Spring MVC框架概述
Spring MVC 是一个基于Java的轻量级Web框架,采用MVC设计模型实现请求驱动的松耦合应用开发。框架包括DispatcherServlet、HandlerMapping、Handler、HandlerAdapter、ViewResolver核心组件。DispatcherServlet协调这些组件处理HTTP请求和响应,Controller处理业务逻辑,Model封装数据,View负责渲染。通过注解@Controller、@RequestMapping等简化开发,支持RESTful请求。Spring MVC具有清晰的角色分配、Spring框架集成、多种视图技术支持以及异常处理等优点。
12 1
|
5天前
|
监控 Java 数据库连接
总结Spring Boot面试知识点
Spring Boot是一个基于Spring框架的开源项目,它简化了Spring应用的初始搭建以及开发过程。通过提供“约定优于配置”的方式,Spring Boot可以帮助开发者快速构建出生产级别的Spring应用。
13 0
|
12天前
|
SQL Java 数据库连接
Springboot框架整合Spring JDBC操作数据
JDBC是Java数据库连接API,用于执行SQL并访问多种关系数据库。它包括一系列Java类和接口,用于建立数据库连接、创建数据库操作对象、定义SQL语句、执行操作并处理结果集。直接使用JDBC涉及七个步骤,包括加载驱动、建立连接、创建对象、定义SQL、执行操作、处理结果和关闭资源。Spring Boot的`spring-boot-starter-jdbc`简化了这些步骤,提供了一个在Spring生态中更便捷使用JDBC的封装。集成Spring JDBC需要添加相关依赖,配置数据库连接信息,并通过JdbcTemplate进行数据库操作,如插入、更新、删除和查询。
|
12天前
|
SQL Java 数据库连接
Springboot框架整合Spring Data JPA操作数据
Spring Data JPA是Spring基于ORM和JPA规范封装的框架,简化了数据库操作,提供增删改查等接口,并可通过方法名自动生成查询。集成到Spring Boot需添加相关依赖并配置数据库连接和JPA设置。基础用法包括定义实体类和Repository接口,通过Repository接口可直接进行数据操作。此外,JPA支持关键字查询,如通过`findByAuthor`自动转换为SQL的`WHERE author=?`查询。