Spring Bean 的生命周期是面试中经常被询问的话题。最近有位读者在面试中未能回答这一问题,导致未通过第一轮。因此,了解这一主题是非常重要的。

为了让文章更具深度,我将内容分为两个主要部分:第一部分涵盖基础知识,帮助大家应对面试,第二部分则深入源码分析,适合对源码感兴趣的读者。

接下来,我们将详细探讨内容大纲。

图片

1. 基础知识

1.1 理解 IoC

IoC(控制反转)是一个重要的概念,它意味着容器负责创建和管理对象,而不是由程序主动创建。此过程体现了面向对象设计中的“好莱坞法则”:“别找我们,我们会找你”。IoC 容器会帮助对象获取所需的依赖,而非由对象主动寻找。

要充分理解 IoC,需要明确以下几个问题:控制关系、控制对象及其反转的原因。

图片

控制关系

在传统的 Java SE 编程中,我们通过 new 关键字主动创建对象,程序控制对象的创建。而在 IoC 模型中,容器负责这一过程,主动管理对象的生命周期。

  • 谁控制谁? IoC 容器控制对象的创建。
  • 控制的内容是什么? 不仅限于对象的创建,还包括对外部资源的管理。

为何称之为反转

反转是相对于传统的直接创建方式而言的。在传统应用中,我们主动控制对象的创建,而在 IoC 中,容器负责对象的查找和注入,形成了“反转”的关系。

1.2 Bean 生命周期

对于原型(Prototype)Bean,当用户通过 getBean 方法获取一个实例时,IoC 容器将不再管理该实例的生命周期,因此实际描述 Bean 生命周期时,主要指的是单例(Singleton)Bean。

图片

Bean 生命周期的主要过程包括:

  1. 实例化:创建 Bean 对象。
  2. 属性赋值:为 Bean 设置属性和依赖。
  3. 初始化:包括多个步骤,从前期准备到后期的初始化,完成后 Bean 可被使用。
  4. 销毁:注册销毁方法,以便在实际销毁时执行清理操作。

整个执行流程可能较为抽象,接下来通过代码进行演示。

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 扩展方法

Bean 生命周期的扩展主要分为四个类别:

  • Aware 接口:允许 Bean 获取容器的资源,例如 BeanNameAwaresetBeanName()BeanFactoryAwaresetBeanFactory()
  • 后处理器:提供初始化前后的处理,例如 BeanPostProcessor 的前置和后置方法。
  • 生命周期接口:定义初始化和销毁方法,例如 InitializingBeanafterPropertiesSet()DisposableBeandestroy()
  • 配置生命周期方法:通过配置文件自定义初始化和销毁方法,例如 init()destroyMethod()

2. 源码解读

注意:本示例基于 5.2.15.RELEASE 版本的 Spring,其他版本可能存在差异。

接下来,我们将深入探讨代码的执行流程。

2.1 代码入口

图片

这里需要多次运行,跳过前面的 beanName 只关注 louzaiBean

图片

进入 doGetBean(),在 getSingleton() 中未找到对象,转入创建 Bean 的逻辑。

图片

2.2 实例化

进入 doCreateBean(),调用 createBeanInstance()

图片

图片

2.3 属性赋值

返回 doCreateBean(),进入 populateBean()。这个方法实现了依赖注入的核心逻辑。

2.4 初始化

继续回到 doCreateBean(),执行 initializeBean()

2.5 销毁

louzaiBean 创建后,销毁过程相对简单。进入示例 LouzaiBean 的方法,执行 destroy() 方法。

3. 写在最后

我们总结几个关键的方法:

  • doCreateBean():整个生命周期的入口。
  • createBeanInstance():用于 Bean 的初始化,调用构造方法。
  • populateBean():实现属性的依赖注入和成员变量初始化。
  • initializeBean():执行初始化相关方法及处理逻辑。
  • destroy():执行销毁相关方法。

对于 populateBean(),其核心在于对象的依赖注入,这是重要的面试知识点之一。