SpringBoot中各種成員的初始化順序及依賴注入中的一些小技巧(靜態(tài)成員注入及@Lazy的使用)

Example

如果我們的類有如下成員變量:

@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.");
  }
}

下面的結(jié)論可以通過在構(gòu)造函數(shù)里打斷點Debug來觀察。

  • 首先初始化的是static成員變量, 此處的count采用默認(rèn)值0。
  • 然后初始化的是非static成員變量,此處的version采用默認(rèn)值0.0。
  • 然后Spring在實例化A時選擇的構(gòu)造函數(shù)的原則是:如果有構(gòu)造函數(shù)被@Autowired所修飾,則采用該構(gòu)造函數(shù)(注意,@Autowired(required = true)只能修飾一個構(gòu)造函數(shù)),否則采用默認(rèn)的無參構(gòu)造函數(shù)。此處采用的構(gòu)造函數(shù)為
    @Autowired
    public A(C c) {
      this.c = c;
      System.out.println("This is A constructor with c argument.");
    }
    
    注意執(zhí)行完該構(gòu)造函數(shù)后,此時的成員變量B并沒有被注入,值還是null。
  • Spring容器選擇合適的Bean注入b (DI階段)。
  • 執(zhí)行被@PostConstruct修飾的init()函數(shù)。

總之,在上面這個例子中,各成員變量的初始化執(zhí)行順序為:“static 成員變量 ”--> “非static成員變量” --> “被@Autowired修飾的構(gòu)造函數(shù)” --> “被@Autowired修飾的成員變量b” --> “被@PostConstruct修飾的init()函數(shù)”。

上述過程也是一個Bean完整生成的過程,因此要注意不同于一般類的實例化,Bean在構(gòu)造函數(shù)完成后還有DI(Dependency Injection)階段,通過Spring容器注入它所依賴的其他Bean,想要在一個Bean生成完后進行其他操作,可以使用@PostConstruct。

Tips

1. 靜態(tài)成員依賴注入

有時我們想要對靜態(tài)成員進行依賴注入(通常是Field dependency injection,即直接在成員上加@Autowired,此種做法不推薦),直接在靜態(tài)成員上加@Autowired是無效的(其值總為null),這是因為靜態(tài)成員變量是類的屬性,不屬于任何對象,而Spring實現(xiàn)Field dependency injection 是要依靠基于實例的reflection(反射)進行的。在這個例子中,Spring通過反射生成bean a, 并且發(fā)現(xiàn)a使用了bean b,然后去生成bean b,再次利用反射生成setter方法將b注入進a,這樣就實現(xiàn)了Field dependency injection。通過上述過程我們可以知道static成員由于不屬于任何實例,所以無法實現(xiàn)這樣的依賴注入,但是我們可以通過Constructor dependency injection(構(gòu)造函數(shù)依賴注入)來實現(xiàn)。以上面的例子為例,Spring在生成bean a(調(diào)用A的構(gòu)造函數(shù))時,由于A的構(gòu)造函數(shù)帶有參數(shù)c,Spring將在容器里尋找是否有符合c類型的bean,找到后將bean c賦值給構(gòu)造函數(shù)的參數(shù)c,然后當(dāng)執(zhí)行到A.c = c時成員變量c就被“注入”成功了。

2. Bean的延遲生成與注入

如果我們希望某個Bean不要在Spring容器啟動時初始化(這樣可以加快應(yīng)用的啟動速度),而是在用到時才實例化,可以用@Lazy這個注解。將這個注解加在@Bean、@Component、@Service、@Configuration等注解上時,這些注解所修飾的Bean將在第一次引用時才實例化。我們舉個簡單的例子:

存在一個普通Bean

@Component
public class CommonBean {
    public CommonBean () {
        System.out.println("This is CommonBean constructor.");
    }
}

以及加了@Lazy的“LazyBean”

@Lazy
@Component
public class LazyBean {
    public LazyBean() {
        System.out.println("This is LazyBean constructor.");
    }
}

假設(shè)由于某種原因,Spring掃描路徑的順序是LazyBean先于CommonBean,如果LazyBean不加@Lazy注解,則Bean的生成順序永遠是LazyBean先于CommonBean;若是加上@Lazy,則CommonBean先生成,并且如果沒有其他地方引用LazyBean(例如沒有其他Bean @Autowired LazyBean),那么LazyBean將一直不進行初始化(直觀表現(xiàn)為它的構(gòu)造函數(shù)一直未被調(diào)用)。在實際應(yīng)用中,如果Bean的數(shù)目比較多,無法立即理清依賴關(guān)系,但確定自己的Bean需要在其他Bean生成之后才生成時,可以通過此方法簡單的控制Bean的生成順序。

注意,若是在CommonBean里引用了LazyBean ,則不論LazyBean有沒有使用@Lazy,它都會先完成實例化,即Bean之間引用的作用會大于@Lazy的作用。

@Lazy同樣可以加在@Autowired注解上,比如

@Component
public class CommonBean {
    @Lazy
    @Autowired
    private LazyBean lazyBean;
}

在CommonBean生成過程中的DI階段,它不關(guān)心LazyBean是否存在于Spring容器中(一般情況下,如果沒有添加@Lazy注解并且沒有類型為LazyBean的Bean在容器中的情況下, 會拋找不到Bean的異常),它會生成一個有同樣接口的代理(由于是代理,沒有真正的功能,但是LazyBean的構(gòu)造函數(shù)及@PostConstruct所修飾的方法都會被調(diào)用),只有真正要調(diào)用LazyBean的方法時,該代理才會在自身內(nèi)部生成LazyBean實例(此時不會再次調(diào)用構(gòu)造函數(shù)及@PostConstruct所修飾的方法,調(diào)用代理的各種方法將會轉(zhuǎn)發(fā)到生成的真正的LazyBean中),這樣也相當(dāng)于實現(xiàn)了延遲加載Bean的功能。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 文章作者:Tyan博客:noahsnail.com 3.4 Dependencies A typical ente...
    SnailTyan閱讀 4,488評論 2 7
  • 夕陽漸落潮水退, 鬢發(fā)星星盼吾歸。 薄暮山色留人醉, 頷首一笑心已回。
    姚小曼閱讀 163評論 0 0
  • 媽媽說我單純的像一張白紙,是一件好事情??晌依斫獠煌浮皢渭儭笔鞘裁匆馑迹惶焯炜鞓烦砷L著,爸爸、媽媽和我給...
    Kitty樂園閱讀 337評論 0 1
  • BTC行情走勢分析:比特幣終于在今天突破上方4050上拉,短期多頭強勢空頭較弱,也正是由于這次的突破,給了整個大盤...
    解義盼閱讀 211評論 0 0
  • 在任何公司,任何行業(yè)。最賺錢只要老板和高管。 所以不管在個公司上班,一定要千方百計的成為高管,或者成為老板。
    斐麗希婭閱讀 179評論 0 0

友情鏈接更多精彩內(nèi)容