阿里云面试:Spring 中 Bean 的生命周期是怎样的?

简介: 阿里云面试:Spring 中 Bean 的生命周期是怎样的?


Spring Bean 的生命周期,面试时非常容易问,这不,前段时间就有个读者去面试,因为不会回答这个问题,一面都没有过。

如果只讲基础知识,感觉和网上大多数文章没有区别,但是我又想写得稍微深入一点。

考虑很多同学不喜欢看源码,我就把文章分为 2 大部分,前面是基础知识,主要方便大家面试和学习 ,后面是源码部分,对源码感兴趣的同学可以继续往后面看。

不 BB,上文章目录。

1. 基础知识

1.1 什么是 IoC ?

IoC,控制反转,想必大家都知道,所谓的控制反转,就是把 new 对象的权利交给容器,所有的对象都被容器控制,这就叫所谓的控制反转。

IoC 很好地体现了面向对象设计法则之一 —— 好莱坞法则:“别找我们,我们找你 ”,即由 IoC 容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。

理解好 IoC 的关键是要明确 “谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”。

谁控制谁,控制什么?

传统 Java SE 程序设计,我们直接在对象内部通过 new 进行创建对象,是程序主动去创建依赖对象。而 IoC 是由专门一个容器来创建这些对象,即由 IoC 容器来控制对象的创建。

  • 谁控制谁?当然是 IoC 容器控制了对象;
  • 控制什么?主要控制了外部资源获取(不只是对象,比如包括文件等)。

为何是反转,哪些方面反转了?

有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转,而反转则是由容器来帮忙创建及注入依赖对象。

  • 为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;
  • 哪些方面反转了?依赖对象的获取被反转了。

1.2 Bean 生命周期

对 Prototype Bean 来说,当用户 getBean 获得 Prototype Bean 的实例后,IOC 容器就不再对当前实例进行管理,而是把管理权交由用户,此后再 getBean 生成的是新的实例。

所以我们描述 Bean 的生命周期,都是指的 Singleton Bean。

Bean 生命周期过程:

  • 实例化 :第 1 步,实例化一个 Bean 对象;
  • 属性赋值 :第 2 步,为 Bean 设置相关属性和依赖;
  • 初始化 :初始化的阶段的步骤比较多,5、6 步是真正的初始化,第 3、4 步为在初始化前执行,第 7 步在初始化后执行,初始化完成之后,Bean 就可以被使用了;
  • 销毁 :第 8~10 步,第 8 步其实也可以算到销毁阶段,但不是真正意义上的销毁,而是先在使用前注册了销毁的相关调用接口,为了后面第 9、10 步真正销毁 Bean 时再执行相应的方法。

整个执行流程稍微有些抽象,下面我们通过代码,来演示执行流程。

1.3 执行流程

创建一个 LouzaiBean。

public class LouzaiBean implements InitializingBean, BeanFactoryAware, BeanNameAware, DisposableBean {
    /**
     * 姓名
     */
    private String name;
    public LouzaiBean() {
        System.out.println("1.调用构造方法:我出生了!");
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
        System.out.println("2.设置属性:我的名字叫"+name);
    }
    @Override
    public void setBeanName(String s) {
        System.out.println("3.调用BeanNameAware#setBeanName方法:我要上学了,起了个学名");
    }
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("4.调用BeanFactoryAware#setBeanFactory方法:选好学校了");
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("6.InitializingBean#afterPropertiesSet方法:入学登记");
    }
    public void init() {
        System.out.println("7.自定义init方法:努力上学ing");
    }
    @Override
    public void destroy() throws Exception {
        System.out.println("9.DisposableBean#destroy方法:平淡的一生落幕了");
    }
    public void destroyMethod() {
        System.out.println("10.自定义destroy方法:睡了,别想叫醒我");
    }
    public void work(){
        System.out.println("Bean使用中:工作,只有对社会没有用的人才放假。。");
    }
}

自定义一个后处理器 MyBeanPostProcessor。

public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("5.BeanPostProcessor.postProcessBeforeInitialization方法:到学校报名啦");
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("8.BeanPostProcessor#postProcessAfterInitialization方法:终于毕业,拿到毕业证啦!");
        return bean;
    }
}

applicationContext.xml 配置文件(部分)。

<bean name="myBeanPostProcessor" class="demo.MyBeanPostProcessor" />
<bean name="louzaiBean" class="demo.LouzaiBean"
      init-method="init" destroy-method="destroyMethod">
    <property name="name" value="楼仔" />
</bean>

测试入口:

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context =new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        LouzaiBean louzaiBean = (LouzaiBean) context.getBean("louzaiBean");
        louzaiBean.work();
        ((ClassPathXmlApplicationContext) context).destroy();
    }
}

执行结果:

1.调用构造方法:我出生了!
2.设置属性:我的名字叫楼仔
3.调用BeanNameAware#setBeanName方法:我要上学了,起了个学名
4.调用BeanFactoryAware#setBeanFactory方法:选好学校了
5.BeanPostProcessor.postProcessBeforeInitialization方法:到学校报名啦
6.InitializingBean#afterPropertiesSet方法:入学登记
7.自定义init方法:努力上学ing
8.BeanPostProcessor#postProcessAfterInitialization方法:终于毕业,拿到毕业证啦!
Bean使用中:工作,只有对社会没有用的人才放假。。
9.DisposableBean#destroy方法:平淡的一生落幕了
10.自定义destroy方法:睡了,别想叫醒我

这个流程非常清晰,Bean 生命周期流程图能完全对应起来。

1.4 扩展方法

我们发现,整个生命周期有很多扩展过程,大致可以分为 4 类:

  • Aware 接口:让 Bean 能拿到容器的一些资源,例如 BeanNameAware 的 setBeanName() ,BeanFactoryAware 的 setBeanFactory()
  • 后处理器:进行一些前置和后置的处理,例如 BeanPostProcessor 的 postProcessBeforeInitialization()postProcessAfterInitialization()
  • 生命周期接口:定义初始化方法和销毁方法的,例如 InitializingBean 的 afterPropertiesSet() ,以及 DisposableBean 的 destroy()
  • 配置生命周期方法:可以通过配置文件,自定义初始化和销毁方法,例如配置文件配置的 init()destroyMethod()

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

2. 源码解读

注意:Spring 的版本是 5.2.15.RELEASE ,否则和我的代码不一样!!!

上面的知识,网上其实都有,下面才是我们的重头戏,让你跟着我走一遍代码流程。

2.1 代码入口

这里需要多跑几次,把前面的 beanName 跳过去,只看 louzaiBean。

进入 doGetBean(),从 getSingleton() 没有找到对象,进入创建 Bean 的逻辑。

2.2 实例化

进入 doCreateBean() 后,调用 createBeanInstance()。

进入 createBeanInstance() 后,调用 instantiateBean()。

走进示例 LouzaiBean 的方法,实例化 LouzaiBean。

图片

2.3 属性赋值

再回到 doCreateBean(),继续往后走,进入 populateBean()。

这个方法非常重要,里面其实就是依赖注入的逻辑,不过这个不是我们今天的重点,大家如果对依赖注入和循环依赖感兴趣,可以翻阅我之前的文章。

进入 populateBean() 后,执行 applyPropertyValues()

进入 applyPropertyValues(),执行 bw.setPropertyValues()

进入 processLocalProperty(),执行 ph.setValue()。

走进示例 LouzaiBean 的方法,给 LouzaiBean 赋值 name。

到这里,populateBean() 就执行完毕,下面开始初始化 Bean。

2.4 初始化

我们继续回到 doCreateBean(),往后执行 initializeBean()。

走进示例 LouzaiBean 的方法,给 LouzaiBean 设置 BeanName。

回到 invokeAwareMethods()。

走进示例 LouzaiBean 的方法,给 LouzaiBean 设置 BeanFactory。

第一次回到 initializeBean() ,执行下面逻辑。

这里需要多循环几次,找到 MyBeanPostProcessor 的策略方法。

我们自己定义的后置处理方法。

第二次回到 initializeBean() ,执行下面逻辑。

走进示例 LouzaiBean 的方法,执行 afterPropertiesSet()。

返回 invokeInitMethods(),执行下面逻辑。

进入 invokeCustomInitMethod(),执行下面逻辑。

走进示例 LouzaiBean 的方法,执行 init()。

第三次回到 initializeBean() ,执行下面逻辑。

我们自己定义的后置处理方法。

到这里,初始化的流程全部结束,都是围绕 initializeBean() 展开。

2.5 销毁

当 louzaiBean 生成后,后面开始执行销毁操作,整个流程就比较简单。

走进示例 LouzaiBean 的方法,执行 destroy()。

回到 destroy(),执行下面逻辑。

走进示例 LouzaiBean 的方法,执行 destroyMethod()。

到这里,所有的流程全部结束,文章详细描述所有的代码逻辑流转,你可以完全根据上面的逻辑,自己 debug 一遍。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

3. 写在最后

我们再回顾一下几个重要的方法:

  • doCreateBean() :这个是入口;
  • createBeanInstance() :用来初始化 Bean,里面会调用对象的构造方法;
  • populateBean() :属性对象的依赖注入,以及成员变量初始化;
  • initializeBean() :里面有 4 个方法,
  • 先执行 aware 的 BeanNameAware、BeanFactoryAware 接口;
  • 再执行 BeanPostProcessor 前置接口;
  • 然后执行 InitializingBean 接口,以及配置的 init();
  • 最后执行 BeanPostProcessor 的后置接口。
  • destory() :先执行 DisposableBean 接口,再执行配置的 destroyMethod()。

对于 populateBean(),里面的核心其实是对象的依赖注入,这里也是常考的知识点,比如循环依赖,大家如果对这块也感兴趣,可以私下和我交流。

image.png

相关文章
|
4天前
|
缓存 Java Spring
实战指南:四种调整 Spring Bean 初始化顺序的方案
本文探讨了如何调整 Spring Boot 中 Bean 的初始化顺序,以满足业务需求。文章通过四种方案进行了详细分析: 1. **方案一 (@Order)**:通过 `@Order` 注解设置 Bean 的初始化顺序,但发现 `@PostConstruct` 会影响顺序。 2. **方案二 (SmartInitializingSingleton)**:在所有单例 Bean 初始化后执行额外的初始化工作,但无法精确控制特定 Bean 的顺序。 3. **方案三 (@DependsOn)**:通过 `@DependsOn` 注解指定 Bean 之间的依赖关系,成功实现顺序控制,但耦合性较高。
实战指南:四种调整 Spring Bean 初始化顺序的方案
|
1月前
|
XML Java 数据格式
Spring从入门到入土(bean的一些子标签及注解的使用)
本文详细介绍了Spring框架中Bean的创建和使用,包括使用XML配置文件中的标签和注解来创建和管理Bean,以及如何通过构造器、Setter方法和属性注入来配置Bean。
62 9
Spring从入门到入土(bean的一些子标签及注解的使用)
|
24天前
|
人工智能 Java API
阿里云开源 AI 应用开发框架:Spring AI Alibaba
近期,阿里云重磅发布了首款面向 Java 开发者的开源 AI 应用开发框架:Spring AI Alibaba(项目 Github 仓库地址:alibaba/spring-ai-alibaba),Spring AI Alibaba 项目基于 Spring AI 构建,是阿里云通义系列模型及服务在 Java AI 应用开发领域的最佳实践,提供高层次的 AI API 抽象与云原生基础设施集成方案,帮助开发者快速构建 AI 应用。本文将详细介绍 Spring AI Alibaba 的核心特性,并通过「智能机票助手」的示例直观的展示 Spring AI Alibaba 开发 AI 应用的便利性。示例源
|
22天前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
|
29天前
|
人工智能 Java API
阿里云开源 AI 应用开发框架:Spring AI Alibaba
阿里云开源 Spring AI Alibaba,旨在帮助 Java 开发者快速构建 AI 应用,共同构建物理新世界。
|
24天前
|
架构师 Java 开发者
得物面试:Springboot自动装配机制是什么?如何控制一个bean 是否加载,使用什么注解?
在40岁老架构师尼恩的读者交流群中,近期多位读者成功获得了知名互联网企业的面试机会,如得物、阿里、滴滴等。然而,面对“Spring Boot自动装配机制”等核心面试题,部分读者因准备不足而未能顺利通过。为此,尼恩团队将系统化梳理和总结这一主题,帮助大家全面提升技术水平,让面试官“爱到不能自已”。
得物面试:Springboot自动装配机制是什么?如何控制一个bean 是否加载,使用什么注解?
|
2月前
|
XML Java 数据格式
spring复习02,xml配置管理bean
详细讲解了Spring框架中基于XML配置文件管理bean的各种方式,包括获取bean、依赖注入、特殊值处理、属性赋值、集合类型处理、p命名空间、bean作用域及生命周期和自动装配。
spring复习02,xml配置管理bean
|
1月前
|
设计模式 缓存 Java
面试题:谈谈Spring用到了哪些设计模式?
面试题:谈谈Spring用到了哪些设计模式?
|
1月前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细解析Spring Bean的生命周期及其核心概念,并深入源码分析。Spring Bean是Spring框架的核心,由容器管理其生命周期。从实例化到销毁,共经历十个阶段,包括属性赋值、接口回调、初始化及销毁等。通过剖析`BeanFactory`、`ApplicationContext`等关键接口与类,帮助你深入了解Spring Bean的管理机制。希望本文能助你更好地掌握Spring Bean生命周期。
67 1
|
1月前
|
Java Spring
获取spring工厂中bean对象的两种方式
获取spring工厂中bean对象的两种方式
32 1
下一篇
无影云桌面