Spring Bean的生命週期

北冥有隻魚發表於2017-12-18
突然覺得問答體很適合解釋一些問題,本篇文章我們接著採用問答故事體的方式來講上面這個問題,這是我之前面試的時候,被問道的。這次來徹底的探討一下這個問題。

Bean 生命週期概念的引入

某一天小陳覺得在這個公司有點厭倦了, 準備悄悄的跑路,於是一邊上班,一邊準備面試題,一邊上班,這天他看到了這樣一個面試題Spring Bean的生命週期,有點理解不動,他想到了他的領導,那個技術棧特別寬的領導,於是就找到了領導問: 領導我對一個Spring Bean生命週期這個概念有點不太理解,您有時間的話可以給我講講嗎?

領導說: 等下,我正在處理一個問題,一會兒,我去找你去。過了十五分鐘後,領導就來到了小陳的工位,說道:現在我們來過Spring Bean的生命週期這個概念吧!準備好了嗎?小陳點了點頭。

領導說道: 首先我們要弄清楚生命週期的含義,生命週期的英文是Life Cycle,簡單的說就是Spring 給我們提供的一些擴充套件介面,如果bean實現了這些這些介面,應用在啟動的過程中會回撥這些介面的方法。在Spring中一個bean的一生通常就是先建立其物件,然後填充其屬性,如果這個Bean實現了Spring 提供的擴充套件介面,那麼在IOC容器載入的時候會依次回撥這些方法。這些擴充套件介面的對應方法的回撥順序如下:

Bean的生命週期

畫完圖後領導問道: 這裡面哪個單詞你熟一些啊, 拿出來講一下。

小陳心裡想領導的這個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的載入順序如下圖(下面這張圖是網上找的)所示:

5922080414274eeba353c0b5d4bede5f

所以我們在寫一個BeanPostProcessor的bean就能實現上面的順序了。至於Bean生命的結束, 其實我是故意考驗你的。其實也就是一個介面和一個屬性而已. 介面是DisposableBean,屬性在@Bean中有個destroyMethod。 這兩個屬性也有替代註解,init-method=@PostConstruct, @PreDestroy=destroyMethod。結合上面的圖我們可以大致瞭解了Spring IOC容器載入Bean的流程,所謂的生命週期在某種程度上可以看成在載入Bean的過程的回撥。

小陳說道: 謝謝領導,我感覺我對這個概念理解的更通透了,相對於生命週期,我更喜歡你回撥的說法呢。

參考資料

相關文章