java 8 Optional

Optional

本章內(nèi)容

  • 如何為缺失的值建模
  • Optional 類
  • 應(yīng)用Optional的幾種模式
  • 使用Optional的實戰(zhàn)實例
  • 小結(jié)

如何為缺失的值建模

exp:

public class Person {
    private Car car;
    public Car getCar() { return car; }
}
/////////
public class Car {
    private Insurance insurance;
    public Insurance getInsurance() { return insurance; }
}
//////////
public class Insurance {
    private String name;
    public String getName() { return name; }
}

如果有這樣一個需求巴比,獲取到用戶給自己車投保的保險公司名稱亚皂。如何獲得驾凶?像下面代碼:

public String getCarInsuranceName(Person person) {
    return person.getCar().getInsurance().getName();
}

但是現(xiàn)實生活中很多人沒有車。所以調(diào)用getCar方法的結(jié)果會怎樣呢痒谴?在實踐中,一種比較常見的做法是返回一個null引用采章,表示該值的缺失惧笛,即用戶沒有車。而接下來甘磨,對getInsurance的調(diào)用會返回null引用的insurance橡羞,這會導(dǎo)致運行時出現(xiàn)
一個NullPointerException,終止程序的運行济舆。但這還不是全部卿泽。如果返回的person值為null會怎樣?如果getInsurance的返回值也是null滋觉,結(jié)果又會怎樣签夭?
采用防御式檢查減少 NullPointerException,exp:

public String getInstanceName1(Person person) {
    if(person != null) {
        Car car = person.getCar();
        if(car != null) {
            Insurance insurance = car.getInsurance();
            if(insurance != null) {
                return insurance.getName();
            }
        }
    }
    return DEFAULT_INSTANCE_NAME;
}

上面代碼清單為“深層質(zhì)疑”椎侠,原因是它不斷重復(fù)著一種模式:每次你不確定一
個變量是否為null時第租,都需要添加一個進一步嵌套的if塊,也增加了代碼縮進的層數(shù)我纪。很明顯慎宾,這種方式不具備擴展性,同時還犧牲了代碼的可讀性浅悉。

解決這種嵌套過深可以使用衛(wèi)語句來解決趟据,exp:

public String getInstanceName2(Person person) {
    if(person == null) {
        return DEFAULT_INSTANCE_NAME;
    }
    Car car = person.getCar();
    if(car == null) {
        return DEFAULT_INSTANCE_NAME;
    }
    Insurance insurance = car.getInsurance();
    if(insurance == null) {
        return DEFAULT_INSTANCE_NAME;
    }
    return insurance.getName();
}

上面代碼雖然解決了嵌套過深問題,然而术健,這種方案遠非理想汹碱,現(xiàn)在這個方法有了四個截然不同的退出點,使得代碼的維護異常艱難荞估。而且這種流程是極易出錯的咳促;如果你忘記檢查了那個可能為null的屬性會怎樣

null 帶來的種種問題

  • 它是錯誤之源色难。
    NullPointerException是目前Java程序開發(fā)中最典型的異常。
  • 它會使你的代碼膨脹等缀。
    它讓你的代碼充斥著深度嵌套的null檢查枷莉,代碼的可讀性糟糕透頂。
  • 它自身是毫無意義的尺迂。
    null自身沒有任何的語義笤妙,尤其是,它代表的是在靜態(tài)類型語言中以一種錯誤的方式對缺失變量值的建模噪裕。
  • 它破壞了Java的哲學(xué)蹲盘。
    Java一直試圖避免讓程序員意識到指針的存在,唯一的例外是: null指針膳音。
  • 它在Java的類型系統(tǒng)上開了個口子召衔。
    null并不屬于任何類型,這意味著它可以被賦值給任意引用類型的變量祭陷。這會導(dǎo)致問題苍凛,原因是當這個變量被傳遞到系統(tǒng)中的另一個部分后,你將無法獲知這個null變量最初的賦值到底是什么類型兵志。

其他語言中null的替代品

  • Groovy : 通過引入安全導(dǎo)航操作符(Safe Navigation Operator醇蝴,標記為?)可以安全訪問可能為null的變量.
    exp : def carInsuranceName = person?.car?.insurance?.name
    Groovy的安全導(dǎo)航操作符能夠避免在訪問這些可能為null引用的變量時拋出NullPointerException,在調(diào)用鏈中的變量遭遇null時將null引用沿著調(diào)用鏈傳遞下去想罕,返回一個null悠栓。
  • Haskell中包含了一個Maybe類型,它本質(zhì)上是對optional值的封裝按价。Maybe類型的變量可以是指定類型的值惭适,也可以什么都不是。但是它并沒有null引用的概念楼镐。Scala有類似的數(shù)據(jù)結(jié)構(gòu)癞志,名字叫Option[T],它既可以包含類型為T的變量鸠蚪,也可以不包含該變量; 要使用這種類型今阳,你必須顯式地調(diào)用Option類型的available操作师溅,檢查該變量是否有值茅信,而這其實也是一種變相的“null檢查”

java8 Optional

汲取Haskell和Scala的靈感, Java 8中引入了一個新的類java.util.Optional<T>墓臭。這是一個封裝Optional值的類蘸鲸。變量存在時, Optional類只是對類簡單封裝窿锉。變量不存在時酌摇,缺失的值會被建模成一個“空”的Optional對象膝舅,由方法Optional.empty()返回。Optional.empty()方法是一個靜態(tài)工廠方法窑多,它返回Optional類的特定單一實例仍稀。

null引用和Optional.empty()有什么本質(zhì)的區(qū)別嗎?

  1. 不會觸發(fā)NullPointerException埂息。
  2. 使用Optional而不是null的一個非常重要而又實際的語義區(qū)別是:如聲明變量時使用的是Optional<Car>類型技潘,而不是Car類型,這句聲明非常清楚地表明了這發(fā)生變量缺失是允許的千康;與此相反享幽,使用Car這樣的類型,可能將變量賦值為null拾弃,這意味著你需要獨立面對這些值桩,你只能依賴你對業(yè)務(wù)模型的理解,判斷一個null是否屬于該變量的有效范疇豪椿。

你的代碼中始終如一地使用Optional奔坟,能非常清晰地界定出變量值的缺失是結(jié)構(gòu)上的問題,還是你算法上的缺陷搭盾,抑或是你數(shù)據(jù)中的問題蛀蜜。另外,我們還想特別強調(diào)增蹭,引入Optional類的意圖并非要消除每一個null引用滴某。與此相反,它的目標是幫助你更好地設(shè)計出普適的API滋迈,讓程序員看到方法簽名霎奢,就能了解它是否接受一個Optional的值。這種強制會讓你更積極地將變量從Optional中解包出來饼灿,直面缺失的變量值幕侠。

應(yīng)用 Optional 的幾種模式

創(chuàng)建Optional對象

  1. 聲明一個空的Optional
Optional<Car> optCar = Optional.empty();
  1. 依據(jù)一個非空值創(chuàng)建Optional
Optional<Car> optCar = Optional.of(car);

如果car是一個null,這段代碼會立即拋出一個NullPointerException碍彭,而不是等到你試圖訪問car的屬性值時才返回一個錯誤晤硕。

  1. 可接受null的Optional
Optional<Car> optCar = Optional.ofNullable(car);
  1. 使用 map 從 Optional 對象中提取和轉(zhuǎn)換值
Optional<Insurance> optInsurance = Optional.ofNullable(insurance);
Optional<String> name = optInsurance.map(Insurance::getName);
  1. 使用 flatMap 從 Optional 對象提取值
Optional<Insurance> optInsurance = Optional.ofNullable(insurance);
String name = optInsurance.flatMap(Insurance::getName);

那對上面的例子可以重新建模 exp:

public class Car {
    private Optional<Insurance> insurance;

    public Optional<Insurance> getInsurance() {
        return insurance;
    }

    public void setInsurance(Optional<Insurance> insurance) {
        this.insurance = insurance;
    }
}
////////////////
public class Person {

    private Optional<Car> car;

    public Optional<Car> getCar() {
        return car;
    }

    public void setCar(Optional<Car> car) {
        this.car = car;
    }
}
//////////////////////
public class Insurance {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

獲取保險公司名稱,如何使用Optional獲取呢庇忌? exp:

public String getInstanceName1(Optional<Person> person) {
    if(person.isPresent()) {
        Optional<Car> car = person.get().getCar();
        if(car.isPresent()) {
            Optional<Insurance> insurance = car.get().getInsurance();
            if(insurance.isPresent()) {
                return insurance.get().getName();
            }
        }
    }
    return UNKNOWN_INSTANCE_NAME;
}

這種方式和null判斷本質(zhì)是一樣的舞箍,沒有解決問題。

**真正的解法 使用flatMap,map 來提取轉(zhuǎn)換值 exp: **

public String getInstanceName2(Optional<Person> person) {
    return person.flatMap(Person::getCar)
            .flatMap(Car::getInsurance)
            .map(Insurance::getName)
            .orElse(UNKNOWN_INSTANCE_NAME);
}

我們決定采用orElse方法讀取這個變量的值皆疹,使用這種方式你還可以定義一個默認值疏橄,遭遇空的Optional變量時,默認值會作為該方法的調(diào)用返回值。Optional類提供了多種方法讀取Optional實例中的變量值捎迫。

  • get() 是這些方法中最簡單但又最不安全的方法晃酒。如果變量存在,它直接返回封裝的變量值窄绒,否則就拋出一個NoSuchElementException異常贝次。所以,除非你非常確定Optional變量一定包含值彰导,否則使用這個方法是個相當糟糕的主意浊闪。此外,這種方式即便相對于嵌套式的null檢查螺戳,也并未體現(xiàn)出多大的改進搁宾。
  • orElse(T other) 它允許你在Optional對象不包含值時提供一個默認值。
  • orElseGet(Supplier<? extends T> other)是orElse方法的延遲調(diào)用版倔幼,Supplier方法只有在Optional對象不含值時才執(zhí)行調(diào)用盖腿。如果創(chuàng)建默認值是件耗時費力的工作,你應(yīng)該考慮采用這種方式(借此提升程序的性能)损同,或者你需要非常確定某個方法僅在Optional為空時才進行調(diào)用翩腐,也可以考慮該方式(這種情況有嚴格的限制條件)。
  • orElseThrow(Supplier<? extends X> exceptionSupplier)和get方法非常類似膏燃,它們遭遇Optional對象為空時都會拋出一個異常茂卦,但是使用orElseThrow你可以定制希望拋出的異常類型
  • ifPresent(Consumer<? super T>) 讓你能在變量值存在時執(zhí)行一個作為參數(shù)傳入的方法,否則就不進行任何操作组哩。

兩個 Optional 對象的組合

假設(shè)你有這樣一個方法等龙,它接受一個Person和一個Car對象,并以此為條件對外
部提供的服務(wù)進行查詢伶贰,通過一些復(fù)雜的業(yè)務(wù)邏輯蛛砰,試圖找到滿足該組合的最便宜的保險公司:

public Insurance findCheapestInsurance(Person person, Car car) {
    // 不同的保險公司提供的查詢服務(wù)
    // 對比所有數(shù)據(jù)
    return cheapestCompany;
}

設(shè)你想要該方法的一個null-安全的版本,它接受兩個Optional對象作為參數(shù)黍衙,
返回值是一個Optional<Insurance>對象泥畅,如果傳入的任何一個參數(shù)值為空,它的返回值亦為空 exp:

public Optional<Insurance> nullSafeFindCheapestInsurance(Optional<Person> person, Optional<Car> car) {
    if (person.isPresent() && car.isPresent()) {
        return Optional.of(findCheapestInsurance(person.get(), car.get()));
    } else {
        return Optional.empty();
    }
}

該方法的具體實現(xiàn)和你之前曾經(jīng)實現(xiàn)的null檢查太相似了,有沒有更優(yōu)雅的方案呢琅翻?
我們可以像使用三元操作符那樣位仁,無需任何條件判斷的結(jié)構(gòu),以一行語句實現(xiàn)該方法方椎,代碼如下聂抢。exp:

public Optional<Insurance> nullSafeFindCheapestInsurance(Optional<Person> person, Optional<Car> car) {
    return person.flatMap(p -> car.map(c -> findCheapestInsurance(p, c)));
}

這段代碼中,你對第一個Optional對象調(diào)用flatMap方法辩尊,如果它是個空值涛浙,傳遞給它的Lambda表達式不會執(zhí)行,這次調(diào)用會直接返回一個空的Optional對象摄欲。反之轿亮,如果person對象存在,這次調(diào)用就會將其作為函數(shù)Function的輸入胸墙,并按照與flatMap方法的約定返回一個Optional<Insurance>對象我注。這個函數(shù)的函數(shù)體會對第二個Optional對象執(zhí)行map操作,如果第二個對象不包含car迟隅,函數(shù)Function就返回一個空的Optional對象但骨,整個nullSafeFindCheapestInsuranc方法的返回值也是一個空的Optional對象。最后智袭,如果person和car對象都存在奔缠,作為參數(shù)傳遞給map方法的Lambda表達式能夠使用這兩個值安全地調(diào)用原始的findCheapestInsurance方法,完成期望的操作吼野。

Optional類的方法

方法 描述
empty 返回一個空的 Optional 實例
filter 如果值存在并且滿足提供的謂詞校哎,就返回包含該值的 Optional 對象;否則返回一個空的Optional 對象
flatMap 如果值存在瞳步,就對該值執(zhí)行提供的 mapping 函數(shù)調(diào)用闷哆,返回一個 Optional 類型的值,否則就返回一個空的 Optional 對象
get 如果該值存在单起,將該值用 Optional 封裝返回抱怔,否則拋出一個 NoSuchElementException 異常
ifPresent 如果值存在,就執(zhí)行使用該值的方法調(diào)用嘀倒,否則什么也不做
isPresent 如果值存在就返回 true屈留,否則返回 false
map 如果值存在,就對該值執(zhí)行提供的 mapping 函數(shù)調(diào)用
of 將指定值用 Optional 封裝之后返回测蘑,如果該值為 null绕沈,則拋出一個 NullPointerException異常
ofNullable 將指定值用 Optional 封裝之后返回,如果該值為 null帮寻,則返回一個空的 Optional 對象
orElse 如果有值則將其返回乍狐,否則返回一個默認值
orElseGet 如果有值則將其返回,否則返回一個由指定的 Supplier 接口生成的值
orElseThrow 如果有值則將其返回固逗,否則拋出一個由指定的 Supplier 接口生成的異常

使用 Optional 的實戰(zhàn)示例

有效地使用Optional類意味著你需要對如何處理潛在缺失值進行全面的反思浅蚪。這種反思不僅僅限于你曾經(jīng)寫過的代碼,更重要的可能是烫罩,你如何與原生Java API實現(xiàn)共存共贏惜傲。實際上,我們相信如果Optional類能夠在這些API創(chuàng)建之初就存在的話贝攒,很多API的設(shè)計編寫可能會大有不同盗誊。為了保持后向兼容性,我們很難對老的Java API進行改動,讓它們也使用Optional哈踱,但這并不表示我們什么也做不了荒适。你可以在自己的代碼中添加一些工具方法,修復(fù)或者繞過這些問題开镣,讓你的代碼能享受Optional帶來的威力刀诬。exp:

  1. 用 Optional 封裝可能為 null 的值,比如從map獲取值
Optional<Object> value = Optional.ofNullable(map.get("key"))

每次你希望安全地對潛在為null的對象進行轉(zhuǎn)換,將其替換為Optional對象時邪财,都可以考慮使用這種方法陕壹。

  1. 異常與 Optional 的對比
    由于某種原因,函數(shù)無法返回某個值树埠,這時除了返回null糠馆, Java API比較常見的替代做法是拋出一個異常。exp: Integer.parseInt(String)如果String無法解析到對應(yīng)的整型怎憋,該方法就拋出一個NumberFormatException;我們可以空的Optional對象又碌,對遭遇無法轉(zhuǎn)換的String時返回的非法值進行建模。exp:
public static Optional<Integer> stringToInt(String s) {
    try {
        return Optional.of(Integer.parseInt(s));
    } catch (NumberFormatException e) {
        return Optional.empty();
    }
}

我們可以將多個類似的方法封裝到一個工具類中,如OptionalUtility.stringToInt.

注: 與 Stream 對象一樣盛霎,Optional也提供了類似的基礎(chǔ)類型——OptionalInt赠橙、 OptionalLong以及OptionalDouble。在Stream的場景愤炸,尤其是如果Stream對象包含了大量元素期揪,出于性能的考量,使用基礎(chǔ)類型是不錯的選擇规个,但對Optional對象而言凤薛,這個理由就不成立了,因為Optional對象最多只包含一個值诞仓。不推薦大家使用基礎(chǔ)類型的Optional缤苫,因為基礎(chǔ)類型的Optional不支持map、flatMap以及filter方法墅拭,而這些卻是Optional類最有用的方法活玲,此外,與Stream一樣谍婉, Optional對象無法由基礎(chǔ)類型的Optional組合構(gòu)成

把所有內(nèi)容整合起來

Properties props = new Properties();
props.setProperty("a", "5");
props.setProperty("b", "true");
props.setProperty("c", "-3");

現(xiàn)在舒憾,我們假設(shè)你的程序需要從這些屬性中讀取一個值,該值是以秒為單位計量的一段時間穗熬。由于一段時間必須是正數(shù)镀迂,你想要該方法符合下面的簽名:

public int readDuration(Properties props, String name)

如果以命令式編程的方式從屬性中讀取duration值,exp:

public int readDuration(Properties props, String name) {
    String value = props.getProperty(name);
    if (value != null) {
        try {
            int i = Integer.parseInt(value);
            if (i > 0) {
                return i;
            }
        } catch (NumberFormatException nfe) { }
    }
    return 0;
}

可以看出最終的實現(xiàn)既復(fù)雜又不具備可讀性唤蔗,呈現(xiàn)為多個由if語句及try/catch
塊兒構(gòu)成的嵌套條件探遵。

以O(shè)ptional來實現(xiàn)呢exp:

public int readDuration(Properties props, String name) {
    return Optional.ofNullable(props.getProperty(name))
        .flatMap(OptionalUtility::stringToInt)
        .orElse(0);
}

Optional 問題

在域模型中使用Optional窟赏,由于沒有實現(xiàn)Serializable 接口,不能進行序列化

小結(jié)

  • null引用在歷史上被引入到程序設(shè)計語言中箱季,目的是為了表示變量值的缺失涯穷。
  • Java 8中引入了一個新的類java.util.Optional<T>,對存在或缺失的變量值進行建模规哪。
  • 你可以使用靜態(tài)工廠方法Optional.empty求豫、Optional.of以及Optional.ofNullable創(chuàng)建Optional對象塌衰。
  • Optional類支持多種方法诉稍,比如map、flatMap最疆、filter杯巨,它們在概念上與Stream類中對應(yīng)的方法十分相似。
  • 使用Optional會迫使你更積極地解引用Optional對象努酸,以應(yīng)對變量值缺失的問題服爷,最終,你能更有效地防止代碼中出現(xiàn)不期而至的空指針異常获诈。
  • 使用Optional能幫助你設(shè)計更好的API仍源,用戶只需要閱讀方法簽名,就能了解該方法是否接受一個Optional類型的值舔涎。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末笼踩,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子亡嫌,更是在濱河造成了極大的恐慌嚎于,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挟冠,死亡現(xiàn)場離奇詭異于购,居然都是意外死亡,警方通過查閱死者的電腦和手機知染,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門肋僧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人控淡,你說我怎么就攤上這事嫌吠。” “怎么了逸寓?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵居兆,是天一觀的道長。 經(jīng)常有香客問我竹伸,道長泥栖,這世上最難降的妖魔是什么簇宽? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮吧享,結(jié)果婚禮上魏割,老公的妹妹穿的比我還像新娘。我一直安慰自己钢颂,他們只是感情好钞它,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著殊鞭,像睡著了一般遭垛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上操灿,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天锯仪,我揣著相機與錄音,去河邊找鬼趾盐。 笑死庶喜,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的救鲤。 我是一名探鬼主播久窟,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼本缠!你這毒婦竟也來了斥扛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤搓茬,失蹤者是張志新(化名)和其女友劉穎犹赖,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體卷仑,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡峻村,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了锡凝。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片粘昨。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖窜锯,靈堂內(nèi)的尸體忽然破棺而出张肾,到底是詐尸還是另有隱情,我是刑警寧澤锚扎,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布吞瞪,位于F島的核電站,受9級特大地震影響驾孔,放射性物質(zhì)發(fā)生泄漏芍秆。R本人自食惡果不足惜惯疙,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望妖啥。 院中可真熱鬧霉颠,春花似錦、人聲如沸荆虱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽怀读。三九已至诉位,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間愿吹,已是汗流浹背不从。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工惜姐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留犁跪,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓歹袁,卻偏偏與公主長得像坷衍,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子条舔,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 這篇文章寫得不錯枫耳,所以轉(zhuǎn)載了下,修改了小部分孟抗,原文地址見末尾 身為一名Java程序員迁杨,大家可能都有這樣的經(jīng)歷:調(diào)用...
    瘋狂的冰塊閱讀 275評論 0 2
  • 轉(zhuǎn)載自 http://www.importnew.com/6675.html 身為一名Java程序員,大家可能都有...
    tenlee閱讀 513評論 0 0
  • Java 8引入了一個新的Optional類凄硼。Optional類的Javadoc描述如下: 這是一個可以為null...
    吧啦啦小湯圓閱讀 1,516評論 0 1
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理铅协,服務(wù)發(fā)現(xiàn),斷路器摊沉,智...
    卡卡羅2017閱讀 134,651評論 18 139
  • 懷斯曼
    風(fēng)一米陽光閱讀 469評論 0 47