Spring Boot教程(22) – 容器中对象的生命周期

你可能在学习其他框架的时候,听到过“生命周期”的概念,通常,框架会提供给你回调方法,在生命周期的关键点,框架会调用这些回调方法。比如你给你的小程序或者安卓应用编写页面的时候,会有类似onHide()、onShow()这种方法,分别在显示(show)页面或者离开(hide)页面的时候调用到,他们正属于页面生命周期的一部分。

在Spring容器中的对象(Bean),也有着自己的生命周期,只不过比较简单一点,因为只有两个关键点。第一个关键点是Bean的所有依赖都已经装配好的时候,另一个是容器即将销毁的时候。你可能会问,容器什么时候销毁?如果你不是自己维护ApplicationContext的话,而是框架自动维护的话,比如使用Spring Boot写web运用,那么你程序停止运行的时候,就是容器销毁的时候。这个时候容器中的对象会根据自己的情况,来释放一些资源,比如关闭它跟外部系统的一些连接等。

这两个关键点的回调,有多种方式可以实现:

  1. 实现InitializingBeanDisposableBean接口
  2. 使用@PostConstruct@PreDestroy注解
  3. @Bean注解添加initMethoddestroyMethod参数
InitializingBean
DisposableBean

InitializingBeanDisposableBean接口分别提供了afterPropertiesSetdestroy方法。@PostConstruct@PreDestroy注解可以分别放到你对象的方法上,这样在回调的时候,添加了注解的方法就会被调用到。@BeaninitMethoddestroyMethod参数是String类型的,你可以将方法的名字传递进去。

方法名可以自定义

从上面这些英文单词的意思中你会意识到,他们其实完成的工作,就是初始化和释放资源的工作。Spring官方文档里说了,不建议使用InitializingBeanDisposableBean,因为他们跟Spring耦合到了一起,不方便以后更换其他IoC容器,而是建议使用@PostConstruct@PreDestroy注解,毕竟他们是标准,是JSR-250的一部分。要我说呀,我既然都选择Spring全家桶了,既然都选择Spring Boot了,我还可能去更换IoC容器么?不可能了呀,所以这两种方式都可以用的,我更倾向于用接口。

Intellij IDEA中可以方便地跳转到方法

@BeaninitMethoddestroyMethod参数,传递的是方法名,这种做法非常不“type-safe”,感觉更像是对以前xml配置方式的兼容。然而他还是有用的,有些Bean并不是由你自己编写的,比如从某个jar包里拿出来的,你不能再去修改类了,只能从外部来设置回调。

我建议你始终保持风格的统一,比如如果你用了InitializingBean,那就在项目里保持一致,都用它。如果实在是不行,也就是说你用到了多种方法,那么你可能需要注意下他们顺序,初始化回调的顺序是:

  • @PostConstruct
  • InitializingBean
  • @Bean的initMethod参数对应方法

在销毁时,顺序跟上面是一致的:

  • @PreDestroy
  • DisposableBean
  • @Bean的destroyMethod参数对应方法

另外值得一提的是,Spring框架有很多*Aware结尾的接口,常见的比如ApplicationContextAwareBeanNameAware,如果你的@Component类实现了ApplicationContextAware,那么容器会调用他的setApplicationContext方法,这样你就可以得到ApplicationContext对象了,你可以拿这个对象查找一些Bean呀等等。同理你可以通过BeanNameAware的setBeanName方法来获取Bean的名字。

还有其他一些*Aware结尾的接口,用的不太多。aware单词的意思是“意识到的、知道的”,*Aware目标就是为了让你的类能“意识到、感觉到”框架里的某些东西。*Aware接口的方法会在对象装配完成之后执行,但是会在@PostConstruct等初始化方法执行之前。如果你是一个业务程序员,或者说CRUD程序员,我不建议你使用*Aware,因为在这种做法将你的对象和Spring容器的细节绑定在一起。如果你是为了给项目写一些基础工具,或者中间件等,倒是可以用用。

我读了很多网上关于Bean生命周期的文章,很多都画了复杂的流程图,如果你碰巧也看过,可以得知,总的流程也就是:装配完=>*Aware执行=>初始化=>用=>销毁。其实很简单是不是?如果你深究,还会发现有很多需要挖掘的东西(比如BeanPostProcessor等),不过由于源码太博大精深,我只能先暂停,补一补知识漏洞再来。

发表评论

电子邮件地址不会被公开。