你可能在学习其他框架的时候,听到过“生命周期”的概念,通常,框架会提供给你回调方法,在生命周期的关键点,框架会调用这些回调方法。比如你给你的小程序或者安卓应用编写页面的时候,会有类似onHide()、onShow()这种方法,分别在显示(show)页面或者离开(hide)页面的时候调用到,他们正属于页面生命周期的一部分。
在Spring容器中的对象(Bean),也有着自己的生命周期,只不过比较简单一点,因为只有两个关键点。第一个关键点是Bean的所有依赖都已经装配好的时候,另一个是容器即将销毁的时候。你可能会问,容器什么时候销毁?如果你不是自己维护 ApplicationContext
的话,而是框架自动维护的话,比如使用Spring Boot写web运用,那么你程序停止运行的时候,就是容器销毁的时候。这个时候容器中的对象会根据自己的情况,来释放一些资源,比如关闭它跟外部系统的一些连接等。
这两个关键点的回调,有多种方式可以实现:
InitializingBean
和 DisposableBean
接口 @PostConstruct
和 @PreDestroy
注解 @Bean
注解添加 initMethod
和 destroyMethod
参数 InitializingBean
和 DisposableBean
接口分别提供了 afterPropertiesSet
和 destroy
方法。 @PostConstruct
和 @PreDestroy
注解可以分别放到你对象的方法上,这样在回调的时候,添加了注解的方法就会被调用到。 @Bean
的 initMethod
和 destroyMethod
参数是String类型的,你可以将方法的名字传递进去。
从上面这些英文单词的意思中你会意识到,他们其实完成的工作,就是初始化和释放资源的工作。 Spring官方文档 里说了,不建议使用 InitializingBean
和 DisposableBean
,因为他们跟Spring耦合到了一起,不方便以后更换其他IoC容器,而是使用 @PostConstruct
和 @PreDestroy
注解,毕竟他们是标准,是 JSR-250 的一部分。要我说呀,我既然都选择Spring全家桶了,既然都选择Spring Boot了,我还可能去更换IoC容器么?不可能了呀,所以这两种方式都可以用的,我更倾向于用接口。
@Bean
有 initMethod
和 destroyMethod
参数,传递的是方法名,这种做法非常不“type-safe”,感觉更像是对以前xml配置方式的兼容。然而他还是有用的,有些Bean并不是由你自己编写的,比如从某个jar包里拿出来的,你不能再去修改类了,只能从外部来设置回调。
我建议你始终保持风格的统一,比如如果你用了 InitializingBean
,那就在项目里保持一致,都用它。如果实在是不行,也就是说上面说到的三种方法都用了,那么你可能需要注意下他们顺序,初始化回调的顺序是:
@PostConstruct InitializingBean @Bean
在销毁时,顺序跟上面是一致的:
@PreDestroy DisposableBean @Bean
另外值得一提的是,Spring框架有很多 *Aware
结尾的接口,常见的比如 ApplicationContextAware
和 BeanNameAware
,如果你的 @Component
类实现了 ApplicationContextAware
,那么容器会调用他的setApplicationContext方法,这样你就可以得到 ApplicationContext
对象了,你可以拿这个对象查找一些Bean呀等等。同理你可以通过 BeanNameAware
的setBeanName方法来获取Bean的名字。
还有其他一些 *Aware
结尾的接口,用的不太多。aware单词的意思是“意识到的、知道的”, *Aware
目标就是为了让你的类能“意识到、感觉到”框架里的某些东西。 *Aware
接口的方法会在对象装配完成之后执行,但是会在 @PostConstruct
等初始化方法执行之前 。如果你是一个业务程序员,或者说CRUD程序员,我不建议你使用 *Aware
,因为在这种做法将你的对象和Spring容器的细节绑定在一起。如果你是为了给项目写一些基础工具,或者中间件等,倒是可以用用。
我读了很多网上关于Bean生命周期的文章,很多都画了复杂的流程图,如果你碰巧也看过,可以得知,总的流程也就是:装配完=> *Aware
执行=>初始化=>用=>销毁。其实很简单是不是?如果你深究,还会发现有很多需要挖掘的东西(比如 BeanPostProcessor
等),不过由于源码太博大精深,我只能先暂停,补一补知识漏洞再来。