激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - 詳解Spring 中如何控制2個bean中的初始化順序

詳解Spring 中如何控制2個bean中的初始化順序

2021-01-19 10:29Chown Java教程

本篇文章主要介紹了Spring 中如何控制2個bean中的初始化順序,具有一定的參考價值,感興趣的小伙伴們可以參考一下

開發過程中有這樣一個場景,2個 bean 初始化邏輯中有依賴關系,需要控制二者的初始化順序。實現方式可以有多種,本文結合目前對 spring 的理解,嘗試列出幾種思路。

場景

假設a,b兩個 bean 都需要在初始化的時候從本地磁盤讀取文件,其中b加載的文件,依賴a中加載的全局配置文件中配置的路徑,所以需要a先于b初始化,此外a中的配置改變后也需要觸發b的重新加載邏輯,所以a,b需要注入彼此。

對于下面的模型,問題簡化為:我們需要inita()先于initb()得到執行。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@service
public class a {
  @autowired
  private b b;
 
  public a() {
    system.out.println("a construct");
  }
 
  @postconstruct
  public void init() {
    inita();
  }
 
  private void inita() {
    system.out.println("a init");
  }
}
 
@service
public class b {
  @autowired
  private a a;
 
  public b() {
    system.out.println("b construct");
  }
 
  @postconstruct
  public void init() {
    initb();
  }
 
  private void initb(){
    system.out.println("b init");
  }
}

方案一:立flag

我們可以在業務層自己控制a,b的初始化順序,在a中設置一個“是否初始化的”標記,b初始化前檢測a是否得以初始化,如果沒有則調用a的初始化方法,所謂的check-and-act。對于上述模型,實現如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@service
public class a {
 
  private static volatile boolean initialized;
 
  @autowired
  private b b;
 
  public a() {
    system.out.println("a construct");
  }
 
  @postconstruct
  public void init() {
    inita();
  }
 
  public boolean isinitialized() {
    return initialized;
  }
 
  public void inita() {
    if (!isinitialized()) {
      system.out.println("a init");
    }
    initialized = true;
  }
}
 
@service
public class b {
 
  @autowired
  private a a;
 
 
  public b() {
    system.out.println("b construct");
  }
 
  @postconstruct
  public void init() {
    initb();
  }
 
 
  private void initb() {
    if (!a.isinitialized()) {
      a.inita();
    }
    system.out.println("b init");
  }

執行效果:

a construct
b construct
a init
b init

這種立flag的方法好處是可以做到lazy initialization,但是如果類似邏輯很多的話代碼中到處充斥著類似代碼,不優雅,所以考慮是否框架本身就可以滿足我們的需要。

方案二:使用dependson

spring 中的 dependson 注解可以保證被依賴的bean先于當前bean被容器創建,但是如果不理解spring中bean加載過程會對 dependson 有誤解,自己也確實踩過坑。對于上述模型,如果在b上加上注解@dependson({"a"}),得到的執行結果是:

a construct
b construct
b init
a init

在這里問題的關鍵是:bean屬性的注入是在初始化方法調用之前。

?
1
2
3
4
5
6
7
// 代碼位置:abstractautowirecapablebeanfactory.docreatebean
// 填充 bean 的各個屬性,包括依賴注入
populatebean(beanname, mbd, instancewrapper);
if (exposedobject != null) {
  // 調用初始化方法,如果是 initializingbean 則先調用 afterpropertiesset 然后調用自定義的init-method 方法
  exposedobject = initializebean(beanname, exposedobject, mbd);
}

結合本例,發生的實際情況是,因為出現了循環依賴,a依賴b,加載b,b依賴a,所以得到了一個提前暴露的a,然后調用b的初始化方法,接著回到a的初始化方法。具體源碼分析過程如下:

applicationcontext 在 refresh 過程中的最后會加載所有的 no-lazy 單例。

詳解Spring 中如何控制2個bean中的初始化順序

本例中,先加載的bean a,最終通過無參構造器構造,然后,繼續屬性填充(populatebean),發現需要注入 bean b。所以轉而加載 bean b(遞歸調用 getbean())。此時發現 bean b 需要 dependson("a"),在保存依賴關系(為了防止循環 depends)后,調用 getbean("a"),此時會得到提前暴露的 bean a ,所以繼續 b 的加載,流程為: 初始化策略構造實例 -> 屬性填充(同樣會注入提前暴露的 bean a ) -> 調用初始化方法。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 代碼位置:abstractbeanfactory.dogetbean
// guarantee initialization of beans that the current bean depends on. 實例化依賴的 bean
    string[] dependson = mbd.getdependson();
    if (dependson != null) {
      for (string dep : dependson) {
        if (isdependent(beanname, dep)) {
          throw new beancreationexception(mbd.getresourcedescription(),
              beanname, "circular depends-on relationship between '"
              + beanname + "' and '" + dep + "'");
        }
        registerdependentbean(dep, beanname); // 緩存 bean 依賴的關系
        getbean(dep);
      }
    }

得到提前暴露的 bean a的過程為:

詳解Spring 中如何控制2個bean中的初始化順序

此時此刻,bean a 的屬性注入完成了, 返回到調用初始化方法,所以表現的行為是:構造a -> 構造b -> b初始化 -> a初始化。

dependson只是保證的被依賴的bean先于當前bean被實例化,被創建,所以如果要采用這種方式實現bean初始化順序的控制,那么可以把初始化邏輯放在構造函數中,但是復雜耗時的邏輯仿造構造器中是不合適的,會影響系統啟動速度。

方案三:容器加載bean之前

spring 框架中很多地方都為我們提供了擴展點,很好的體現了開閉原則(ocp)。其中 beanfactorypostprocessor 可以允許我們在容器加載任何bean之前修改應用上下文中的beandefinition(從xml配置文件或者配置類中解析得到的bean信息,用于后續實例化bean)。

在本例中,就可以把a的初始化邏輯放在一個 beanfactorypostprocessor 中。

?
1
2
3
4
5
6
7
@component
public class abeanfactorypostprocessor implements beanfactorypostprocessor {
  @override
  public void postprocessbeanfactory(configurablelistablebeanfactory configurablelistablebeanfactory) throws beansexception {
    a.inita();
  }
}

執行效果:

a init
a construct
b construct
b init

這種方式把a中的初始化邏輯放到了加載bean之前,很適合加載系統全局配置,但是這種方式中初始化邏輯不能依賴bean的狀態。

方案四:事件監聽器的有序性

spring 中的 ordered 也是一個很重要的組件,很多邏輯中都會判斷對象是否實現了 ordered 接口,如果實現了就會先進行排序操作。比如在事件發布的時候,對獲取到的 applicationlistener 會先進行排序。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 代碼位置:abstractapplicationeventmulticaster.listenerretriever.getapplicationlisteners()
public collection<applicationlistener<?>> getapplicationlisteners() {
    linkedlist<applicationlistener<?>> alllisteners = new linkedlist<applicationlistener<?>>();
    for (applicationlistener<?> listener : this.applicationlisteners) {
      alllisteners.add(listener);
    }
    if (!this.applicationlistenerbeans.isempty()) {
      beanfactory beanfactory = getbeanfactory();
      for (string listenerbeanname : this.applicationlistenerbeans) {
        try {
          applicationlistener<?> listener = beanfactory.getbean(listenerbeanname, applicationlistener.class);
          if (this.prefiltered || !alllisteners.contains(listener)) {
            alllisteners.add(listener);
          }
        } catch (nosuchbeandefinitionexception ex) {
          // singleton listener instance (without backing bean definition) disappeared -
          // probably in the middle of the destruction phase
        }
      }
    }
    annotationawareordercomparator.sort(alllisteners); // 排序
    return alllisteners;
  }

所以可以利用事件監聽器在處理事件時的有序性,在應用上下文 refresh 完成后,分別實現a,b中對應的初始化邏輯。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@component
public class applicationlistenera implements applicationlistener<applicationcontextevent>, ordered {
  @override
  public void onapplicationevent(applicationcontextevent event) {
    inita();
  }
 
  @override
  public int getorder() {
    return ordered.highest_precedence; // 比 applicationlistenerb 優先級高
  }
 
  public static void inita() {
    system.out.println("a init");
  }
}
 
@component
public class applicationlistenerb implements applicationlistener<applicationcontextevent>, ordered{
  @override
  public void onapplicationevent(applicationcontextevent event) {
    initb();
  }
 
  @override
  public int getorder() {
    return ordered.highest_precedence -1;
  }
 
  private void initb() {
    system.out.println("b init");
  }
}

執行效果:

a construct
b construct
a init
b init

這種方式就是站在事件響應的角度,上下文加載完成后,先實現a邏輯,然后實現b邏輯。

總結

在平時的開發中使用的可能都是一個語言,一個框架的冰山一角,隨著對語言,對框架的不斷深入,你會發現更多的可能。本文只是基于目前對于 spring 框架的理解做出的嘗試,解決一個問題可能有多種方式,其中必然存在權衡選擇,取決于對業務對技術的理解。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:https://zhuanlan.zhihu.com/p/30112785?utm_source=tuicool&utm_medium=referral

延伸 · 閱讀

精彩推薦
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25
主站蜘蛛池模板: 成人免费毛片在线观看 | 国产呻吟 | 黄污网站在线观看 | 欧美亚洲综合网 | 91在线观看| 国产一区网址 | 97超视频在线观看 | 欧美亚州| 亚洲免费视 | 日韩视频在线不卡 | 久久蜜臀一区二区三区av | 成人午夜在线播放 | 视频一区二区视频 | 人人看人人舔 | 99欧美精品 | h视频在线免费观看 | 中文亚洲视频 | 久久网站热最新地址 | 国产毛片aaa一区二区三区视频 | 久久久成人免费视频 | 最新毛片在线观看 | 中国洗澡偷拍在线播放 | 国产黄色一区二区 | 日产精品久久久一区二区福利 | 亚洲视频成人 | 亚洲一区二区三区四区精品 | 日日草夜夜草 | 中国黄色一级生活片 | 精品一区二区久久久久久按摩 | 成人免费观看av | 精品三级内地国产在线观看 | 好吊色欧美一区二区三区四区 | 欧美一区二区三区久久精品视 | 欧美乱淫| 久久成人激情视频 | 中文字幕 在线观看 | 成人午夜视屏 | 久久精品99久久久久久2456 | 91成人午夜性a一级毛片 | 91精选视频 | 国产一区二区三区高清 |