Spring學習筆記:Bean初始化

本文是自己學習的一個總結


1、bean初始化簡介

1.1、bean的初始化發生在什麼階段,做了什麼事

當我們從xml文件或者註解中生成容器時,XML文件或者註解中描述的bean就完成了初始化。

所謂初始化,就是bean的元信息加載進容器,說具體也就是bean信息由xml文件或者註解中讀取出來,加載爲BeanDefinition,再通過BeanDefinitionRegistry將這些BeanDefiniton註冊到容器中的過程。

其中,這個初始化過程又可以分爲構造->屬性填充->初始化完成,這三個階段又分別對應着接下來的三類回調函數。


2、Bean初始化的回調函數

2.1、基於@PostConstruct,構造後回調函數

2.1.1、使用@PostConstruct

@PostConstruct,從名字上看就能知道,是bean完成初始化之後的回調註解。該註解的使用方式是在被定義成Bean的類A中實現一個方法,並用@PostContruct標註這個方法,那麼這個方法就是類A作爲Bean完成初始化之後的回調方法。類A作爲bean在容器中初始化之後,會調用被@PostContruct標註的方法。

我們看看例子。
DefaultUserFactory的實現如下,這是我們要註冊爲Bean的類。

@Component
public class DefaultUserFactory {
    @PostConstruct
    public void init() {
        System.out.println("@PostConstruct:UserFactory 初始化");
    }
}

之後在和DefaultUserFactory同一個包下定義掃描類。

@ComponentScan
public class ConfigScan {}

最後根據掃描類生成容器,我們看系統對DefaultUserFactory完成初始化後會不會調用init()函數。

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigScan.class);

最後打印結果如下,回調函數成功調用。
在這裏插入圖片描述

2.1.2、同一個類中使用多個@PostConstruct

@PostConstruct在類中使用標註方法,當這個類被初始化成bean之後就會調用@PostConstruct標註的方法。

其中,@PostConstruct可以在類中標註多個方法,並且類被初始化成bean之後,所有被@PostConstruct標註的方法都會被回調,但是調用的順序不能保證,並不是按定義順序調用的,系統似乎有自己的一套規則。

2.1.3、註解生效範圍

@PostConstruct在類A 中使用,那只有類A是通過註解的方式初始化成bean時,@PostConstruct纔會生效。

但是如果類A是通過XML初始化成bean,那@PostConstruct就不會起作用。


2.2、實現InitInitializingBean覆寫afterPropertiesSet

若類A要被註冊爲bean,那可令類A實現InitInitializingBean接口,覆寫其中的afterPropertiesSet方法。這樣A作爲bean在初始化階段中,屬性填充以後,會回調覆寫的afterPropertiesSet方法。

基於上面的代碼,我們在DefaultUserFactory中加入afterPropertiesSet相關的代碼

@Component
public class DefaultUserFactory implements UserFactory, InitializingBean {
    @PostConstruct
    public void init() {
        System.out.println("@PostConstruct:UserFactory 初始化");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet:DefaultUserFactory初始化");
    }
}

掃描類和生成容器的代碼不變。

public class ConfigScan {}
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigScan.class);

最後輸出結果如下
在這裏插入圖片描述

2.2.1、afterPropertiesSet的生效範圍

實現InitInitializingBean覆寫afterPropertiesSet,這個方法與@PostConstruct不同,無論是通過註解還是通過xml,afterPropertiesSet都可以生效。


2.3、基於@Bean的initMethod屬性,初始化後回調函數

@Bean中有個屬性是initMethod,他是用來將類中的某個方法指定爲初始化函數。我們看看例子。

接着上面的代碼,我們在DefaultUserFactory中加入@Bean相關的代碼

@Component
public class DefaultUserFactory implements UserFactory{
    @PostConstruct
    public void init() {
        System.out.println("@PostConstruct:UserFactory 初始化");
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet:DefaultUserFactory初始化");
    }
    @Bean(initMethod = "initFactory")
    public DefaultUserFactory getDefaultUserFactory() {
        return new DefaultUserFactory();
    }
    public void initFactory() {
        System.out.println("@Bean.initMethod:DefaultUserFactory初始化");
    }
}

可以預測,最後生成的容器中有兩個類型爲DefaultUserFactory的Bean。一個是基於@Component生成的bean,這個bean只設置了@PostConstruct和afterProperties的回調函數;另一個是基於@Bean生成的bean,這個類不僅有@PostConstruct和afterProperties的回調函數,還有initMethod的回調函數。

掃描類和生成容器的代碼不變。

public class ConfigScan {}
ApplicationContext applicationContext = new 
AnnotationConfigApplicationContext(ConfigScan.class);

最後輸出結果如下

在這裏插入圖片描述

2.3.1、initMethod的生效範圍

因爲這也是基於註解實現的,所以只有容器是通過註解生成時initMethod纔有效,通過xml生成容器時initMethod會無效。

2.4、@PostConstruct,afterPropertiesSet和initMethod的執行順序

文章開頭說過,bean的初始化又分爲構造->屬性填充->初始化,而以上三種方式就對應着這三個階段。

@PostConstruct在構造結束後會被調用,afterPropertiesSet在屬性填充後會被調用,initMethod在初始化完成後會被調用。





3、bean的延遲初始化

3.1、bean設置延遲初始化的方式

一般來說說只有兩種,一種是xml中設置,另一種是註解中設置。

  • <bean lazy-init=‘true’ ···/>
  • @Lazy(true)

3.2、延遲初始化的時機和非延遲初始化的時機

延遲初始化是在容器啓動之後,需要實例化bean時纔會初始化這個bean,而非延遲初始化則是在容器啓動是就完成初始化。

我們在上面代碼的基礎上給DefaultUserFactory加上@Lazy註解

@Lazy
public class DefaultUserFactory implements InitializingBean{···}

同時在啓動容器的語句之後加上一段輸出語句表示容器啓動了,然後實例化defaultUserFactory。

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigScan.class);
System.out.println("容器啓動了");
applicationContext.getBean("defaultUserFactory");

最後輸出語句是
在這裏插入圖片描述
可以看出,defaultUserFactory是在容器啓動後,需要實施化時纔去初始化。

如果DefaultUserFactory沒有@Lazy註解,輸出語句則是下圖所示,初始化在容器啓動時。

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章