突然覺得問答體很適合解釋一些問題,本篇文章我們接著採用問答故事體的方式來講上面這個問題,這是我之前面試的時候,被問道的。這次來徹底的探討一下這個問題。
Bean 生命週期概念的引入
某一天小陳覺得在這個公司有點厭倦了, 準備悄悄的跑路,於是一邊上班,一邊準備面試題,一邊上班,這天他看到了這樣一個面試題Spring Bean的生命週期,有點理解不動,他想到了他的領導,那個技術棧特別寬的領導,於是就找到了領導問: 領導我對一個Spring Bean生命週期這個概念有點不太理解,您有時間的話可以給我講講嗎?
領導說: 等下,我正在處理一個問題,一會兒,我去找你去。過了十五分鐘後,領導就來到了小陳的工位,說道:現在我們來過Spring Bean的生命週期這個概念吧!準備好了嗎?小陳點了點頭。
領導說道: 首先我們要弄清楚生命週期的含義,生命週期的英文是Life Cycle,簡單的說就是Spring 給我們提供的一些擴充套件介面,如果bean實現了這些這些介面,應用在啟動的過程中會回撥這些介面的方法。在Spring中一個bean的一生通常就是先建立其物件,然後填充其屬性,如果這個Bean實現了Spring 提供的擴充套件介面,那麼在IOC容器載入的時候會依次回撥這些方法。這些擴充套件介面的對應方法的回撥順序如下:
畫完圖後領導問道: 這裡面哪個單詞你熟一些啊, 拿出來講一下。
小陳心裡想領導的這個PPT畫的怎麼這麼熟練,還是接著回答道:我好像只見過ApplicationContext,我剛學Spring 框架的時候,用這個ApplicatonContext來獲取載入進IOC容器的物件,像下面這樣:
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringBeanConfig.class);
applicationContext.getBean(StudentService.class);
}
領導點了點頭,接著說道: 大多數入門都是從這樣的例子開始的,說道這裡看著上面的程式碼你想到了設計模式的哪個模式?
小陳回答道: 這讓我想起了簡單工廠模式,我給一個標識,由ApplicationContext給我載入對應的物件。
領導點了點頭: ApplicationContext 繼承自BeanFactory, 上面的確是使用了簡單工廠模式。上面介面中帶有Aware的, 都繼承自Aware介面。子類將對於父類來說提供的功能更為豐富。setBeanName會從換入實現BeanNameAware介面的Bean Id和Bean名稱,也就是說容器在初始化Bean和填充完Bean的屬性之後,會回依次回撥BeanNameAware、ApplicationContextAware、BeanPostProcessor的postProcessBeforeInitialization、InitializingBean的afterPropertiesSet方法,Bean的init-method方法、BeanPostProcessor的postProcessAfterInitialization的方法。這裡要注意BeanNameAware、ApplicationContextAware只會回撥依次,每個Bean完成初始化之後都會回撥BeanPostProcessor的兩個方法。到這裡其實就結束了。下面我們寫個程式碼來演示一下上面的順序:
public class BeanLifeCycleDemo implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean,BeanPostProcessor{
private MuttonSoupService muttonSoupService;
public BeanLifeCycleDemo( ) {
System.out.println("1.建構函式初始化bean");
}
@Autowired
public void setMuttonSoupService(MuttonSoupService muttonSoupService) {
System.out.println("2.先填充屬性-");
this.muttonSoupService = muttonSoupService;
}
public void sayHelloWorld(){
System.out.println("hello world");
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("----4-- 接著是BeanFactoryAware");
}
@Override
public void setBeanName(String name) {
System.out.println("3---- name+" + name);
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("----7 afterPropertiesSet");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("------5 applicationContextAware");
}
public void initMethod(){
System.out.println("6 init-method");
}
/**
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("---- postProcessBeforeInitialization"+beanName);
return bean;
}
/**
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("---- postProcessAfterInitialization");
return bean;
}
}
@Configuration
public class SpringBeanConfig {
@Bean(initMethod = "initMethod")
public BeanLifeCycleDemo beanLifeCycleDemo(){
return new BeanLifeCycleDemo();
}
}
@SpringBootTest
class SsmApplicationTests {
@Autowired
private BeanLifeCycleDemo beanLifeStyleDemo;
// 直接執行測試即可
@Test
public void test(){
}
}
輸出結果:
小陳看到了這個結果, 心裡有點開心,領導翻車了,於是說道:領導你這個順序跟上面圖的順序不一樣欸。我還有一個問題,這個Bean是永生的嗎?
領導淡淡的說道:我是故意演示給你看的, 注意這個BeanPostProcessor,這個介面對應的Bean需要預先載入進入才能實現上面的效果。Spring Bean的載入順序如下圖(下面這張圖是網上找的)所示:
所以我們在寫一個BeanPostProcessor的bean就能實現上面的順序了。至於Bean生命的結束, 其實我是故意考驗你的。其實也就是一個介面和一個屬性而已. 介面是DisposableBean,屬性在@Bean中有個destroyMethod。 這兩個屬性也有替代註解,init-method=@PostConstruct, @PreDestroy=destroyMethod。結合上面的圖我們可以大致瞭解了Spring IOC容器載入Bean的流程,所謂的生命週期在某種程度上可以看成在載入Bean的過程的回撥。
小陳說道: 謝謝領導,我感覺我對這個概念理解的更通透了,相對於生命週期,我更喜歡你回撥的說法呢。
參考資料
- Spring – Bean Life Cycle https://howtodoinjava.com/spr...
- Spring用到哪些設計模式 https://199604.com/2188
- BeanPostProcessor —— 連線Spring IOC和AOP的橋樑 https://zhuanlan.zhihu.com/p/...