SpringBoot中各類成員的初始化順序及靜態成員的依賴注入

Example

若是咱們的類有以下成員變量:html

@Component
public class A {

  @Autowired
  public B b;  // B is a bean

  public static C c;  // C is also a bean

  public static int count;

  public float version;

  public A() {
    System.out.println("This is A constructor.");
  }

  @Autowired
  public A(C c) {
    A.c = c;
    System.out.println("This is A constructor with c argument.");
  }

  @PostConstruct
  public void init() {

    count = 5;
    System.out.println("This is A post construct.");
  }
}

下面的結論能夠經過在構造函數裏打斷點Debug來觀察。java

  • 首先初始化的是static成員變量, 此處的count採用默認值0。
  • 而後初始化的是非static成員變量,此處的version採用默認值0.0。
  • 而後Spring在實例化A時選擇的構造函數的原則是:若是有構造函數被@Autowired所修飾,則採用該構造函數(注意,@Autowired(required = true)只能修飾一個構造函數),不然採用默認的無參構造函數。此處採用的構造函數爲spring

    @Autowired
    public A(C c) {
      this.c = c;
      System.out.println("This is A constructor with c argument.");
    }

    注意執行完該構造函數後,此時的成員變量B並無被注入,值仍是null。api

  • Spring容器選擇合適的Bean注入b。
  • 執行被@PostConstruct修飾的init()函數。

總之,在上面這個例子中,各成員變量的執行順序爲:「static 成員變量 」--> 「非static成員變量」 --> 「被@Autowired修飾的構造函數」 --> 「被@Autowired修飾的成員變量b」 --> 「被@PostConstruct修飾的init()函數」。函數

Tips

  1. 有時咱們想要對靜態成員進行依賴注入(一般是Field dependency injection,即直接在成員上加@Autowired,此種作法不推薦),直接在靜態成員上加@Autowired是無效的(其值總爲null),這是由於靜態成員變量是類的屬性,不屬於任何對象,而Spring實現Field dependency injection 是要依靠基於實例的reflection(反射)進行的。在這個例子中,Spring經過反射生成bean a, 而且發現a使用了bean b(此時bean b已經生成並被註冊到Spring容器中),再次利用反射生成setter方法並將b set進a,這樣就實現了Field dependency injection。經過上述過程咱們能夠知道static成員因爲不屬於任何實例,因此沒法實現這樣的依賴注入,可是咱們能夠經過Constructor dependency injection(構造函數依賴注入)來實現。以上面的例子爲例,Spring在生成bean a(調用A的構造函數)時,因爲A的構造函數帶有參數c,Spring將在容器裏尋找是否有符合c類型的bean,找到後將bean c賦值給構造函數的參數c,而後當執行到A.c = c時成員變量c就被「注入」成功了。
  2. 若是咱們但願某個Bean不要在Spring容器啓動時初始化(這樣能夠加快應用的啓動速度),而是在用到時才實例化,能夠用@Lazy這個註解。將這個註解加在@Bean、@Component、@Service、@Configuration等註解上時,這些註解所修飾的Bean將在第一次引用時才實例化;若是在@Autowired上也同時加上這個註解,則該Bean將在第一次使用時實例化。咱們再舉個簡單的例子:

@Component等註解上加@Lazypost

@Lazy
@Component
public class LazyBean {
  public LazyBean() {
    System.out.println("This is LazyBean constructor.");
  }
}
* 在UseBean裏經過```@Autowired```注入LazyBean,不加```@Lazy```:

  ```java
  @Component
  public class UseBean {
    @Autowired
    private LazyBean lazyBean;
    public UseBean () {}
  }
  ```
當應用啓動時,Spring要去掃描這些被```@Component```等註解修飾的類,當即將他們實例化並註冊到容器中,可是因爲LazyBean 類被```@Lazy```修飾,Spring會跳過這個Bean的實例化。當生成UseBean後(即Spring完成對UseBean的構造函數的調用後),因爲UseBean引用了LazyBean,這個時候Spring纔將LazyBean實例化。所以,以上Bean的初始化順序永遠是先初始化UseBean,當執行到```@Autowired private LazyBean lazyBean;```時才實例化lazyBean。

* 在``` @Component```等註解和```@Autowired```上都加```@Lazy```

 ```java
  @Getter
  @Component
  public class UseBean {
    @Lazy
    @Autowired
    private LazyBean lazyBean;
    public UseBean () {}

    @PostConstruct
    public void init() {
      System.out.println(this.getLazyBean());
    }
  }
  ```
這種狀況下即便執行到```@Autowired private LazyBean lazyBean;```時也沒有真正實例化LazyBean ,只有在真正使用lazyBean時,即上述代碼中的```this.getLazyBean()```時纔開始調用LazyBean 的構造函數來實例化。
相關文章
相關標籤/搜索