java|Bean 生命周期详解

Spring Bean 的生命周期,面试时非常容易问,这不,前段时间就有个粉丝去字节面试,因为不会回答这个问题,一面都没有过。
如果只讲基础知识,感觉和网上大多数文章没有区别,但是我又想写得稍微深入一点。
考虑很多同学不喜欢看源码,我就把文章分为 2 大部分,前面是基础知识,主要方便大家面试和学习,后面是源码部分,对源码感兴趣的同学可以继续往后面看。
不 BB,上文章目录。
java|Bean 生命周期详解
文章图片

1. 基础知识 1.1 什么是 IoC ? IoC,控制反转,想必大家都知道,所谓的控制反转,就是把 new 对象的权利交给容器,所有的对象都被容器控制,这就叫所谓的控制反转。
IoC 很好地体现了面向对象设计法则之一 —— 好莱坞法则:“别找我们,我们找你”,即由 IoC 容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。
理解好 IoC 的关键是要明确 “谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”。
java|Bean 生命周期详解
文章图片

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

  • 谁控制谁?当然是 IoC 容器控制了对象;
  • 控制什么?主要控制了外部资源获取(不只是对象,比如包括文件等)。
为何是反转,哪些方面反转了?
有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转,而反转则是由容器来帮忙创建及注入依赖对象。
  • 为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;
  • 哪些方面反转了?依赖对象的获取被反转了。
1.2 Bean 生命周期 对 Prototype Bean 来说,当用户 getBean 获得 Prototype Bean 的实例后,IOC 容器就不再对当前实例进行管理,而是把管理权交由用户,此后再 getBean 生成的是新的实例。
所以我们描述 Bean 的生命周期,都是指的 Singleton Bean。
java|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 配置文件(部分)。

测试入口:
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()。
2. 源码解读
注意:Spring 的版本是 5.2.15.RELEASE,否则和我的代码不一样!!!
上面的知识,网上其实都有,下面才是我们的重头戏,让你跟着我走一遍代码流程。
2.1 代码入口 java|Bean 生命周期详解
文章图片

java|Bean 生命周期详解
文章图片

这里需要多跑几次,把前面的 beanName 跳过去,只看 louzaiBean。
java|Bean 生命周期详解
文章图片

java|Bean 生命周期详解
文章图片

进入 doGetBean(),从 getSingleton() 没有找到对象,进入创建 Bean 的逻辑。
java|Bean 生命周期详解
文章图片

java|Bean 生命周期详解
文章图片

2.2 实例化 进入 doCreateBean() 后,调用 createBeanInstance()。
java|Bean 生命周期详解
文章图片

进入 createBeanInstance() 后,调用 instantiateBean()。
java|Bean 生命周期详解
文章图片

java|Bean 生命周期详解
文章图片

java|Bean 生命周期详解
文章图片

java|Bean 生命周期详解
文章图片

java|Bean 生命周期详解
文章图片

走进示例 LouzaiBean 的方法,实例化 LouzaiBean。
java|Bean 生命周期详解
文章图片

2.3 属性赋值 再回到 doCreateBean(),继续往后走,进入 populateBean()。
【java|Bean 生命周期详解】这个方法非常重要,里面其实就是依赖注入的逻辑,不过这个不是我们今天的重点,大家如果对依赖注入和循环依赖感兴趣,可以翻阅我之前的文章。
java|Bean 生命周期详解
文章图片

进入 populateBean() 后,执行 applyPropertyValues()
java|Bean 生命周期详解
文章图片

进入 applyPropertyValues(),执行 bw.setPropertyValues()
java|Bean 生命周期详解
文章图片

java|Bean 生命周期详解
文章图片

java|Bean 生命周期详解
文章图片

java|Bean 生命周期详解
文章图片

进入 processLocalProperty(),执行 ph.setValue()。
java|Bean 生命周期详解
文章图片

java|Bean 生命周期详解
文章图片

java|Bean 生命周期详解
文章图片

走进示例 LouzaiBean 的方法,给 LouzaiBean 赋值 name。
java|Bean 生命周期详解
文章图片

到这里,populateBean() 就执行完毕,下面开始初始化 Bean。
2.4 初始化 我们继续回到 doCreateBean(),往后执行 initializeBean()。
java|Bean 生命周期详解
文章图片

java|Bean 生命周期详解
文章图片

java|Bean 生命周期详解
文章图片

走进示例 LouzaiBean 的方法,给 LouzaiBean 设置 BeanName。
java|Bean 生命周期详解
文章图片

回到 invokeAwareMethods()。
java|Bean 生命周期详解
文章图片

走进示例 LouzaiBean 的方法,给 LouzaiBean 设置 BeanFactory。
java|Bean 生命周期详解
文章图片

第一次回到 initializeBean(),执行下面逻辑。
java|Bean 生命周期详解
文章图片

这里需要多循环几次,找到 MyBeanPostProcessor 的策略方法。
java|Bean 生命周期详解
文章图片

我们自己定义的后置处理方法。
java|Bean 生命周期详解
文章图片

第二次回到 initializeBean(),执行下面逻辑。
java|Bean 生命周期详解
文章图片

java|Bean 生命周期详解
文章图片

走进示例 LouzaiBean 的方法,执行 afterPropertiesSet()。
java|Bean 生命周期详解
文章图片

返回 invokeInitMethods(),执行下面逻辑。
java|Bean 生命周期详解
文章图片

进入 invokeCustomInitMethod(),执行下面逻辑。
java|Bean 生命周期详解
文章图片

走进示例 LouzaiBean 的方法,执行 init()。
java|Bean 生命周期详解
文章图片

第三次回到 initializeBean(),执行下面逻辑。
java|Bean 生命周期详解
文章图片

java|Bean 生命周期详解
文章图片

我们自己定义的后置处理方法。
java|Bean 生命周期详解
文章图片

到这里,初始化的流程全部结束,都是围绕 initializeBean() 展开。
2.4 销毁 当 louzaiBean 生成后,后面开始执行销毁操作,整个流程就比较简单。
java|Bean 生命周期详解
文章图片

java|Bean 生命周期详解
文章图片

java|Bean 生命周期详解
文章图片

java|Bean 生命周期详解
文章图片

java|Bean 生命周期详解
文章图片

java|Bean 生命周期详解
文章图片

java|Bean 生命周期详解
文章图片

java|Bean 生命周期详解
文章图片

java|Bean 生命周期详解
文章图片

java|Bean 生命周期详解
文章图片

走进示例 LouzaiBean 的方法,执行 destroy()。
java|Bean 生命周期详解
文章图片

回到 destroy(),执行下面逻辑。
java|Bean 生命周期详解
文章图片

java|Bean 生命周期详解
文章图片

java|Bean 生命周期详解
文章图片

走进示例 LouzaiBean 的方法,执行 destroyMethod()。
java|Bean 生命周期详解
文章图片

到这里,所有的流程全部结束,文章详细描述所有的代码逻辑流转,你可以完全根据上面的逻辑,自己 debug 一遍。
3. 写在最后 我们再回顾一下几个重要的方法:
  • doCreateBean():这个是入口;
  • createBeanInstance():用来初始化 Bean,里面会调用对象的构造方法;
  • populateBean():属性对象的依赖注入,以及成员变量初始化;
  • initializeBean():里面有 4 个方法,
    • 先执行 aware 的 BeanNameAware、BeanFactoryAware 接口;
    • 再执行 BeanPostProcessor 前置接口;
    • 然后执行 InitializingBean 接口,以及配置的 init();
    • 最后执行 BeanPostProcessor 的后置接口。
  • destory():先执行 DisposableBean 接口,再执行配置的 destroyMethod()。
对于 populateBean(),里面的核心其实是对象的依赖注入,这里也是常考的知识点,比如循环依赖,大家如果对这块也感兴趣,可以私下和我交流。
今天的源码解析就到这,Spring 相关的源码,还有哪些是大家想学习的呢,可以给楼仔留言。
这篇文章肝了我一个星期,原创不易,大家的点赞和分享,是我继续创作的最大动力!

    推荐阅读