一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

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

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

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

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

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

開發(fā)過程中有這樣一個(gè)場(chǎng)景,2個(gè) bean 初始化邏輯中有依賴關(guān)系,需要控制二者的初始化順序。實(shí)現(xiàn)方式可以有多種,本文結(jié)合目前對(duì) spring 的理解,嘗試列出幾種思路。

場(chǎng)景

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

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

?
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

我們可以在業(yè)務(wù)層自己控制a,b的初始化順序,在a中設(shè)置一個(gè)“是否初始化的”標(biāo)記,b初始化前檢測(cè)a是否得以初始化,如果沒有則調(diào)用a的初始化方法,所謂的check-and-act。對(duì)于上述模型,實(shí)現(xiàn)如下:

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

執(zhí)行效果:

a construct
b construct
a init
b init

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

方案二:使用dependson

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

a construct
b construct
b init
a init

在這里問題的關(guān)鍵是:bean屬性的注入是在初始化方法調(diào)用之前。

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

結(jié)合本例,發(fā)生的實(shí)際情況是,因?yàn)槌霈F(xiàn)了循環(huán)依賴,a依賴b,加載b,b依賴a,所以得到了一個(gè)提前暴露的a,然后調(diào)用b的初始化方法,接著回到a的初始化方法。具體源碼分析過程如下:

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

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

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

?
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. 實(shí)例化依賴的 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 依賴的關(guān)系
        getbean(dep);
      }
    }

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

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

此時(shí)此刻,bean a 的屬性注入完成了, 返回到調(diào)用初始化方法,所以表現(xiàn)的行為是:構(gòu)造a -> 構(gòu)造b -> b初始化 -> a初始化。

dependson只是保證的被依賴的bean先于當(dāng)前bean被實(shí)例化,被創(chuàng)建,所以如果要采用這種方式實(shí)現(xiàn)bean初始化順序的控制,那么可以把初始化邏輯放在構(gòu)造函數(shù)中,但是復(fù)雜耗時(shí)的邏輯仿造構(gòu)造器中是不合適的,會(huì)影響系統(tǒng)啟動(dòng)速度。

方案三:容器加載bean之前

spring 框架中很多地方都為我們提供了擴(kuò)展點(diǎn),很好的體現(xiàn)了開閉原則(ocp)。其中 beanfactorypostprocessor 可以允許我們?cè)谌萜骷虞d任何bean之前修改應(yīng)用上下文中的beandefinition(從xml配置文件或者配置類中解析得到的bean信息,用于后續(xù)實(shí)例化bean)。

在本例中,就可以把a(bǔ)的初始化邏輯放在一個(gè) beanfactorypostprocessor 中。

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

執(zhí)行效果:

a init
a construct
b construct
b init

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

方案四:事件監(jiān)聽器的有序性

spring 中的 ordered 也是一個(gè)很重要的組件,很多邏輯中都會(huì)判斷對(duì)象是否實(shí)現(xiàn)了 ordered 接口,如果實(shí)現(xiàn)了就會(huì)先進(jìn)行排序操作。比如在事件發(fā)布的時(shí)候,對(duì)獲取到的 applicationlistener 會(huì)先進(jìn)行排序。

?
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;
  }

所以可以利用事件監(jiān)聽器在處理事件時(shí)的有序性,在應(yīng)用上下文 refresh 完成后,分別實(shí)現(xiàn)a,b中對(duì)應(yīng)的初始化邏輯。

?
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 優(yōu)先級(jí)高
  }
 
  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");
  }
}

執(zhí)行效果:

a construct
b construct
a init
b init

這種方式就是站在事件響應(yīng)的角度,上下文加載完成后,先實(shí)現(xiàn)a邏輯,然后實(shí)現(xiàn)b邏輯。

總結(jié)

在平時(shí)的開發(fā)中使用的可能都是一個(gè)語言,一個(gè)框架的冰山一角,隨著對(duì)語言,對(duì)框架的不斷深入,你會(huì)發(fā)現(xiàn)更多的可能。本文只是基于目前對(duì)于 spring 框架的理解做出的嘗試,解決一個(gè)問題可能有多種方式,其中必然存在權(quán)衡選擇,取決于對(duì)業(yè)務(wù)對(duì)技術(shù)的理解。

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。

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

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 毛片一级毛片 | 亚洲香蕉综合在人在线视看 | 亚洲精品免费视频 | 99久久综合给久久精品 | 欧美一级乱妇老太婆特黄 | 欧美肥胖老妇做爰变态 | 欧美综合在线 | 草榴色导航 | 成人在线视频观看 | 小舞丝袜调教喷水沦为肉奴 | 天天视频国产精品 | 国产小视频在线播放 | 99热这里只精品99re66 | 4399h漫画| 国产精品66福利在线观看 | 日本一区二区在线不卡 | 久久热在线视频精品店 | 丰满的闺蜜2中文字幕 | 手机看片日韩1024你懂的首页 | 1024免费永久福利视频 | 暗卫调教女主肉高h | 2018av在线| 操bb| 4438全国免费观看 | 国产毛片一级aaaaa片 | 亚洲精品久久麻豆蜜桃 | 九九热精品免费观看 | 亚洲v日韩v欧美在线观看 | 黑帮少爷爱上我第8集最新 荷兰精品女人性hd 和日本免费不卡在线v | 小向美奈子av | 免费精品99久久国产综合精品 | 亚洲卡一卡2卡三卡4卡无卡三 | 校草太大了h | 动漫jk美女被爆羞羞漫画 | 欧美福利二区 | 亚洲系列第一页 | 国产青青操 | 日韩拍拍拍 | 精品久久久久国产免费 | 5g影院天天 | 色综合合久久天天综合绕视看 |