標籤: 大陸快遞台灣

  • 極*Java速成教程 – (1)

    序言

    眾所周知,程序員需要快速學習新知識,所以就有了《21天精通C++》和《MySQL-從刪庫到跑路》這樣的書籍,Java作為更“高級”的語言也不應該落後,所以我就寫一個《極·Java速成教程》,讓想學的人能夠快速學習Java(手斜)。
    本文大概分三大部分:

    • 編程基礎知識
      • 面向對象思想
      • 數據類型
      • 運算符
      • 流程控制
    • Java語言基礎
    • Java高級特性

    本文請逐字理解文本內容,本文僅供理解和學習Java編程,不可作為概念性參考

    本文是閱讀《Java編程思想》后的讀後感
    本文着重於理解某種思想,所以些許描述顯得晦澀,意會即可

    面向對象思想

    何為面向對象

    Java是一個從設計之初就遵循面向對象思想的語言,因此而有着諸多面向對象的特性,那麼問題來了,面向對象指的是什麼?
    面向對象,不是指的臉超女朋友,而是說編程的時候,你所操作的一切事物都是“對象”,假如說Java程序員是個搬磚工,那Java操作的對象就是搬磚工手裡的磚,一塊磚就是一個對象,一堆轉就是一堆對象,諸多同樣的對象可以用一個概念來概括,這個概念就是“”。
    先在我們可以思考磚是個什麼東西,也就是磚的“概念(類)”:磚,由土燒制而成,可以用來砌牆、鋪地和墊桌角,有一定的重量和體積,能被搬被砌。仔細分析這句定義,可以發現能分為4小句:

    • 磚是由土燒制而成。也就是說,一個類都是從另一個或多個類繼承發展而來,也就是“繼承(extend)和組合自另一個或多個類”,在Java中,所有類都繼承和擴展自Object類,在自己寫的程序中,一個類可以繼承和擴展自官方的類,別人寫的類和自己寫的類。
    • 磚可以用來砌牆、鋪地和墊桌角。也就是同一個類可以根據不同的需求去加工處理,修改為不同的“形狀”,添加不同的屬性,在不同的地方起到不同的作用。
    • 磚由一定的重量和體積。也就是說每個類都有其屬性,但是對於同一種屬性,他們的值未必相同,正如每個磚的重量和體積都可以不同。
    • 磚能被搬被砌。也就是說每個類都有其功能,屬性是一個類靜態的描述,而功能(或者說方法)則是一個類動態的描述。

    將許多個“磚”一樣的對象搭配組合,堆壘到一起,就可以構建起程序的這棟房屋,實現整體的功能。

    Java中類的幾個特點

    Java中的類有幾個特點

    1. 單根繼承,也就是說某一個類只能直接繼承自另一個類,就像磚只能由土燒制而成,即土→磚,然而除了土,磚的燒制過程中還需要加其他的配料比如外面要燒上去一層琉璃,那就得在磚本身的屬性以上添加一個琉璃的屬性,琉璃的屬性是額外的,獨立無關的,但是他們組合起來實現了更多的功能。也就是說除了直接繼承,還可以添加其他類到一個類中,這種方法叫通過接口(interface)進行繼承。
    2. 訪問控制。對於一個類來說,你只能看到一個類想讓你看到的部分。就像一個磚,你只能看到磚的表面,不能看到它的內里,除非敲開他,但是在Java中,這是不被允許的。一個類想讓你看到的部分和不想讓你看到的部分由不同的關鍵詞進行控制:
      • public 對於所有對象都公開的內容
      • private 只有自己可見的內容
      • protected 只有自己和自己的子孫可以看到的內容
      • default 只有自己的親戚(同一個包)可以看到的內容
        我們不用關心他們內部什麼樣子,我們只需要關心我們需要的,暴露在外面的東西,以及對象能正常工作就行。
    3. “像”就是“是”。對於一個類來說,如果長得像一個東西,那就可以看作是一個東西。就像兒子像父親,父親能幹的兒子也能幹一樣,一塊防火轉是一個磚,那他也有磚的所有屬性,也能被壘起來。而一個具有壘起來方法的對象看上去能壘起來,那他就能像磚一樣被壘起來。
    4. static屬性。對於一個類來說,有的屬性和方法可以是static也就是靜態的,這種屬性和方法不是描述一個類的對象的,而是描述一個類的。就比如可以對所有的磚執行統計數量操作,對於一個磚頭讓他統計同類的數量是不恰當的,但是對於一堆轉,讓這堆磚告訴你有多少塊是可行的。
    5. 對象生命周期管理。Java語言的使用者不用關心Java對象是怎麼誕生的,怎麼消亡的,只需要在需要時new一下,創建一個對象即可,使用完以後的垃圾丟到哪裡,那是Java高級特性需要研究的東西。
    6. Java中的類之上具有包(package)的結構,包是具有層次的,如java.lang.Math就表示java下的lang包下的Math類,這個層次結構可以有好多層,一層一層深入,所以可能這個名字會很長。在使用某一個包中的某一個類的某些內容時,需要使用import進行導入,如果需要導入某個包下的全部,可以使用*通配符。,如java.lang.*。

    Java編程需要關心的點

    只要想好這個類和它的對象應該怎麼設計,怎麼正常運行即可,其他語言的包袱,甩掉即可

    數據類型

    一切都是對象,而根基只需要幾個

    當Java使用者需要一個對象時,這個對象在內存中的位置時我們不用管也沒法管的。但這個對象是怎麼來的,卻是可以思考的。就像房子是磚壘成的,磚是由土燒成的,那土是什麼構成的呢,這麼追究下去,追究到最後就成了重子和輕子了,但是我們不必追求這麼細,就像Java中我們知道一切對象都有Object這個“祖宗”,但是我們不需要凡事都請動這個祖宗,它有幾個子女是我們常用的,也就是“基本類型”:

    類型 大小 包裝器 初始值
    boolean true/false Boolean false
    char 16 bit Character ‘\u0000’
    byte 8 bit Byte (byte)0
    short 16 bit Short (short)0
    int 32 bit Integer 0
    long 64 bit Long 0L
    float 32 bit Float 0.0f
    double 64 bit Double 0.0d
    void Void void

    這些基本類型的大小都與其位數n有關,最小值為-2n-1,最大值為2n-1-1,float和double遵循IEEE754標準。
    除了基本類型,還有字符串String,高精度整數BigInteger和高精度浮點數BigDecimal等常見類。

    還有一些方便的類型可以使用

    String

    字符串是一個常用的類型,可以用來儲存一個字符序列。字符串類型在內存中是一個不可變對象,也就是說當一個String s="ABC"字符串在內存中創建后,它就不可以被修改了,如果執行了s+="DEF"的操作,那這個s引用實際上是指向了一個新的”ABCDEF”對象,它與原來的”ABC”對象已經沒有關係了。
    字符串變量的比較是不可以直接用==判斷的。==是用來進行引用對比的運算符,而兩個內容一樣的字符串引用未必在內存中指向同一個對象。因此,String對象的比較要使用equals()方法進行。

    enum

    enum是枚舉對象,是一個有限的可以n選1的對象的集合,可以在switch語句內使用。形如:

    public enum Spiciness{
        NOT,MILD,MEDIUM,HOT,FLAMING
    }

    枚舉類型的實例都是常量,可以使用枚舉類型名.枚舉常量的方法選擇常量的值。它內部創建了toString()方法,所以可以很方便地打印枚舉常量的值,還提供了ordinal()方法,可以獲取某個枚舉常量在枚舉類型中的順序。以及靜態的 values()方法,可以按照枚舉常量的聲明順序獲取由這些常量值構成的數組。

    對象應當被管理

    一個對象,給他個稱號,就可以根據這個名字喊他做各種事,就像家裡人可以喊你小名讓你幹活一樣。但是當出了家裡,可能有好多人跟你重名,這會帶來困擾,因此是不被允許的,而在外面叫你的小名,這也會帶來尷尬,也就是說,這個名字超出了作用域,在Java中,一個稱號(或者說引用)的作用域是從這個命名的聲明到所在花括號的結尾。當然如果不關心這一點也沒問題,因為IDE會提醒你這裏出了問題。
    當一個對象有了它的稱呼以後,就可以安排他做好多的事情了,無論誰喊,只要是喊到,就可以用。在作用域範圍內的所有調用都可以通過這個名字實行,而你也可以再給這個對象起另一個名字,這多個名字都是指的同一個對象,當處理不當時,這會帶來一些問題,比如一個喊你張三的人給你圖了個花臉,別人喊你李四把你喊過來以後也會看到一個花臉。
    對於一堆相同類型的對象,可以用一個容器裝住他們,方便我們管理和使用,Java中提供了多種類型的容器,有最基本的數組,也就是一串連續的空間去裝一樣的對象,還有其他的諸如set和map之類的容器可以裝,這一點可以在後面進行詳細討論。

    運算符

    運算符按照字面語義理解就好

    運算符有其優先級,也有短路的特徵。優先級就是說先乘除后加減,短路就是邏輯運算時當直接可以產生結果時就不再繼續運算。
    運算符有以下幾個:
    +。加號,数字的累加,字符串的連接,將較小類型的数字提升為int類型。
    -。減號,数字相減。負號。
    *。乘號,数字相乘。
    /。除號,数字相除,給出結果的整數部分,直接截斷小數,不會四舍五入。
    %。求余,数字相除求餘數。
    ++自增,–自減,當出現在對象前面時先運算再輸出值,當出現在對象後面時先輸出值再運算。
    >,<,>=,<=。大於,小於,大於等於,小於等於。顧名思義。
    ==判斷相等,對於Java來說,這是容易使人困擾的一點,因為除了在判斷基本類型對象是否相等時以外,==僅僅為判斷別名所指向的對象是否為同一個,而不關心對象的屬性是否相等。如果要判斷String等官方封裝好的類型,就要用到equals()方法,但是對於自己定義的類來說,如果沒有重新定義equals()方法,就會和==一樣,對引用對象進行判斷,而不是對對象的屬性進行判斷。
    &&,||,!。邏輯運算符與或非,對boolean值進行判斷,兩邊都true時&&結果為真,兩邊有一個為true時||結果為真,!是對boolean對象的邏輯值進行顛倒。
    &,|,^,~。按位操作符,對数字類型進行按位的與,或,異或和非操作,與,或,異或是對兩個對象進行操作,非是對一個對象進行操作。
    <<,>>,>>>。移位操作符,就是將操作數按位進行移位,A※※B就是將A往箭頭方向移B位。如果是負數,>>在左側補1,>>>在左側補0。
    =。数字賦值,或者是給一個對象起別名,或者是將另一個別名添加到一個對象上。
    +=,-=,*=,/=,&=,|=,^=,>>=,>>>=,<<=。比較類似,a※=b就是a=a※b。當>>>=byte和short時,轉化成int進行處理然後截斷,可能產生不正確的結果。
    指數計數法:AeB代表了Ax10B,如1.39e-43f代表float的1.39×10-43
    ?:。這是個三元運算符,如A?B:C就表示如果A的邏輯結果為真,就執行B,否則就執行C。
    直接常量,就是直接的數,如200D,0x200。其規則是如果後面跟D表示double,F表示float,L表示long,前面跟0表示8進制數,0x表示16進制數。

    操作符使用時容易出現的問題

    類型轉換

    (A)B可以將B強制轉化為A類型,如果是從較小的類型轉化為較大的類型,將不會帶來問題,但如果是從較大的類型轉化為較小的類型,則會帶來信息丟失的問題。自定義類的轉換同理。

    截尾和舍入

    對於浮點數來說,強制轉化為整形時,會捨棄小數部分,如果要獲取捨入的結果,需要使用java.lang.Math中的round()方法。

    流程控制

    Java是一種面向對象的語言,但其執行過程依舊時順序的,在順序執行的程序中,會進行一些選擇分支和循環操作。

    選擇

    Java中可以使用if-else和switch進行分支選擇。

    if-else

    形如

    if(A){
        B
    }else{
        C
    }

    就是如果判斷條件A為真,則執行B,否則執行C。

    switch

    形如

    switch(A){
        case 1: B;
        case 2: C;break;
        case 3: D;break;
        case 4: E;break;
    ···
    }

    即如果判斷條件A為1,則執行B然後執行C然後中斷該代碼塊,如果判斷條件為2則執行C然後中斷該代碼塊,如果判斷條件為3則執行D然後中斷該代碼塊,等等。

    循環

    循環有3種形式:while,do-while和for。

    while

    形如

    while(A){
        B
    }
    對判斷條件A進行判斷,當判斷條件為A時,多次執行B代碼。

    do-while

    形如

    do{
        B
    }while(A)

    先執行一次B代碼,然後對判斷條件A進行判斷,當判斷條件A為真時,循環至花括號頭部執行B代碼。

    for

    形如

    for(int i=0,j=0;i<100;i++,j++){
        B
    }

    首先對第一個;前面的部分執行,常見於循環控制對象進行初始化,多個對象可以使用,進行區分,然後對;間的循環控制條件進行判斷,如果條件為真則執行下方代碼塊,最後對;后的第三部分執行,比如對循環對象進行自增操作等。實例中就是先對i和j進行初始化為0,然後判斷i是否小於100,如果為真則執行B代碼,最後執行i自增和j自增,然後判斷i是否小於100,如果為真繼續循環,該代碼最後將執行B代碼段100次。
    在編程時,循環常在數組等可迭代對象上進行。可迭代就是可以從前到后順序走一遍。對此,Java提供了一些便利的語法方便開發。形如

    容器/數組 A;
    for(int i:A){
        B
    }

    就是說對於容器或者數組A,迭代遍歷一遍,i會在每次循環中引用到A中的每個對象,然後執行B代碼塊。假如容器/數組A中有100個對象,那B就會被執行100次。

    返回

    return

    結束該語句所在函數,返回返回值。
    返回值類型由函數定義決定,如果不匹配且無法兼容則會IDE報錯。

    Java的與眾不同

    Java的流程控制中沒有goto語句,但是通過break和continue語句,實現了從深層循環中跳出的功能。通過在循環前標記label:,然後使用break labelcontinue label,即可跳出循環到指定位置或者是跳出本次循環到指定位置。

    label1:
    while(true){
        label2:
        while(true){
            continue label1;//結束本次循環,跳轉到外循環繼續循環
            break label1://結束外層循環
        }
    }

    感慨

    Java的基本語法,與其他的語言有相似之處,也有不同之處,但編程本來就是一種實踐的技能,紙上得來終覺淺,絕知代碼要手擼,還是實打實的寫代碼才可以將知識轉化為能力。以及,文章真難寫。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

    ※專營大陸快遞台灣服務

    台灣快遞大陸的貨運公司有哪些呢?

  • 中國汽車工業三十強榜單發佈:冠軍上汽集團營收超萬億元

    中國汽車工業三十強榜單發佈:冠軍上汽集團營收超萬億元

    2016年5月26日,中國機械工業聯合會、中國汽車工業協會發佈最新中國汽車工業三十強榜單。上汽集團以2015年1.2萬億元的總營業收入繼續蟬聯冠軍,一汽和東風依然排第2、3位。

    榜單中,蓋世汽車盤點出有乘用車公司16家,商用車公司4家,零部件公司7家,摩托車公司3家。本次榜單基於“年度匯總口徑快報”統計2015年營業收入,和上市公司合併財務報表中的營業收入存在計算上的差別,後者抵消了關聯交易部分。這也是為何上汽2015年年報中營收為670,448,223,139.34元,而本榜單中為1.2萬億元的緣故。

    對比2015年排名(以2014年營業收入統計),19家企業排名沒有變化,比亞迪和威孚高科新上榜,陝西汽車和金城集團落榜。前十名中,僅長城(今年第9)和重汽(今年第10)互換了名次,其他8家企業都保持了去年的座次。提升最快的是中鼎,從第27提高到第24,上升3位。下降幅度最大的是法士特,從第24滑落至第28。

    冠軍上汽集團去年營收總額超過第2名一汽和第3名東風總和。

    以下是具體排名

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

    ※專營大陸快遞台灣服務

    台灣快遞大陸的貨運公司有哪些呢?

  • 大眾汽車計畫投資100億歐元新建一座電池工廠

    據外媒報導,大眾汽車計畫投資100億歐元新建一座電池工廠,意欲轉型為一家領先的電動汽車製造商,從而使其可以自己掌控電動汽車的命運,而不是單純依賴于亞洲的電池廠商。

    在大眾汽車6月22日召開年度股東大會以前,該公司的一個非執行理事會將對首席執行官馬蒂亞斯•穆勒(Matthias Müller)及其下屬一個團隊提出的計畫進行考慮。根據《商報》的報導,這項計畫的規模將需要足夠大到可以支援一個目標,即在未來十年時間裡將純電動汽車(PEV)的銷售量提高至100萬輛。

    從投資額來看,大眾汽車計畫建設的這座工廠類似於特斯拉汽車的“超級工廠”(Gigafactry),而該公司所需電池的規模也意味著這將是一座龐大的工廠,但大眾汽車尚未透露更多細節。

    據報導,大眾汽車的董事會很可能將會批准這項計畫,而身為該公司大股東之一的德國下薩克森州(Lower Saxony)也將支持該計畫。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

    ※專營大陸快遞台灣服務

    台灣快遞大陸的貨運公司有哪些呢?

  • 三、netcore跨平台之 Linux配置nginx負載均衡

    三、netcore跨平台之 Linux配置nginx負載均衡

    前面兩章講了netcore在linux上部署以及配置nginx,並讓nginx代理webapi。

    這一章主要講如何配置負載均衡,有些步驟在前兩章講的很詳細了,所以這一章我就不會一個個截圖了。

    因為本人只有一個服務器。所以我會在同一台服務器上部署兩套差不多的實例。

    同樣的代碼,我們在Program.cs進行了修改,如圖所示:

    這裏我把原來的端口6666改成了8888

     

     然後你可以改一改你的接口部分的代碼,便於讓你更好的看到效果。

    這裏把value1和value2改成value3和value4,這裡是為了看到測試效果,在實際的開發中這裏不用改。

     

     然後發布和上傳到服務器,如何發布和上傳,我在第一章有講到:https://www.cnblogs.com/dengbo/p/11878766.html

    注意的是你同樣的地方新建一個新的目錄保存你新上傳的程序,netcore是我第一章建立的,netcore1是新建的,

    你把你新的發布包放在netcore即可。如圖:

    上傳結束后,在這個目錄中運行你的程序,輸入下面的命令

    dotnet WebApiTest.dll   --server.urls "http://*:8888"

    如圖所示

     

     然後去看看你的接口是否正常

     

     

    好了,這裏的準備工作完成了,下面我們進入到nginx的配置的目錄中

    輸入下面的命令:

    cd /usr/local/nginx/conf

    然後對文件進行編輯

    vim nginx.conf

     

     我們需要在這裏修改一下配置。

    在如圖的server的平級添加如下的代碼

    upstream NgWebApi {
                    server localhost:6666;
                    server localhost:8888;
        }

    上面的 NgWebApi是隨意寫的名稱,不要糾結這裏。

    然後在修改 proxy_pass後面的內容:

    proxy_pass http://NgWebApi;

    最終的結果如下:

     

     這樣你就修改完成,輸入:wq退出並保存即可。

    最後檢查並重啟nginx

    /usr/local/nginx/sbin/nginx -t
    /usr/local/nginx/sbin/nginx -s reload

    最後不要忘記把你的8888端口的webapi啟動一下。

    這裏我務必要提醒你,請進入到你的程序的目錄中執行這段代碼,

    cd /root/netcore1
    dotnet WebApiTest.dll   --server.urls "http://*:8888"

    啟動如下:

     

     

     好了,配置結束了,下面我們來測試下

     

    還是昨天的那個網站進行測試   https://www.sojson.com/httpRequest/

     

     

     

    多次發送請求會出現下面的響應

     

     

    看到上面兩個請求,就說明你配置成功了,是不是很簡單。

    上面這種配置,系統會採用默認的輪詢訪問不同的端口,nginx作為強大的反向代理,強大的遠遠不止這裏

    下面簡單講講分發策略。

    1)、輪詢 ——輪流處理請求(這是系統默認的)

          每個請求按時間順序逐一分配到不同的應用服務器,如果應用服務器down掉,自動剔除它,剩下的繼續輪詢,如果您的服務器都差不多,建議這個。 

    2)、權重 ——誰的設置的大,誰就承擔大部分的請求

          通過配置權重,指定輪詢幾率,權重和訪問比率成正比,用於應用服務器性能不均的情況,有時候你買的服務器可能參差不齊,有的性能強大

        有的一般,你可以通過設置權重,把服務器性能強大權重設置大一點,這樣可以合理分配壓力。 

    3)ip_哈希算法

          每一次的請求按訪問iphash結果分配,這樣每個訪客固定訪問一個應用服務器,可以解決session共享的問題。

     

     

    關於權重的策略,如下圖示的 你只要加一個  weight=6 即可這裏不一定是6,是整數都行。

     

     

     然後保存即可

    這裏不要忘記重啟nginx,以及運行8888端口的程序了,如果你不會,可以看前面的部分

    最後我們看看效果

    結果和上面的測試結果差不多,唯一不同的是出現下面這個結果的次數要大於另外一個的。

     

     

    到這裏就結束了,感謝觀看。

     

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

    ※專營大陸快遞台灣服務

    台灣快遞大陸的貨運公司有哪些呢?

  • SpringBoot 源碼解析 (六)—– Spring Boot的核心能力 – 內置Servlet容器源碼分析(Tomcat)

    SpringBoot 源碼解析 (六)—– Spring Boot的核心能力 – 內置Servlet容器源碼分析(Tomcat)

    Spring Boot默認使用Tomcat作為嵌入式的Servlet容器,只要引入了spring-boot-start-web依賴,則默認是用Tomcat作為Servlet容器:

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    Servlet容器的使用

    默認servlet容器

    我們看看spring-boot-starter-web這個starter中有什麼

    核心就是引入了tomcat和SpringMvc,我們先來看tomcat

    Spring Boot默認支持Tomcat,Jetty,和Undertow作為底層容器。如圖:

    而Spring Boot默認使用Tomcat,一旦引入spring-boot-starter-web模塊,就默認使用Tomcat容器。

    切換servlet容器

    那如果我么想切換其他Servlet容器呢,只需如下兩步:

    • 將tomcat依賴移除掉
    • 引入其他Servlet容器依賴

    引入jetty:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <exclusions>
            <exclusion>
                <!--移除spring-boot-starter-web中的tomcat-->
                <artifactId>spring-boot-starter-tomcat</artifactId>
                <groupId>org.springframework.boot</groupId>
            </exclusion>
        </exclusions>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <!--引入jetty-->
        <artifactId>spring-boot-starter-jetty</artifactId>
    </dependency>

    Servlet容器自動配置原理

    EmbeddedServletContainerAutoConfiguration

    其中
    EmbeddedServletContainerAutoConfiguration是嵌入式Servlet容器的自動配置類,該類在
    spring-boot-autoconfigure.jar中的web模塊可以找到。

    我們可以看到EmbeddedServletContainerAutoConfiguration被配置在spring.factories中,看過我前面文章的朋友應該知道SpringBoot自動配置的原理,這裏將EmbeddedServletContainerAutoConfiguration配置類加入到IOC容器中,接着我們來具體看看這個配置類:

    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
    @Configuration
    @ConditionalOnWebApplication// 在Web環境下才會起作用
    @Import(BeanPostProcessorsRegistrar.class)// 會Import一個內部類BeanPostProcessorsRegistrar
    public class EmbeddedServletContainerAutoConfiguration {
    
        @Configuration
        // Tomcat類和Servlet類必須在classloader中存在 // 文章開頭我們已經導入了web的starter,其中包含tomcat和SpringMvc // 那麼classPath下會存在Tomcat.class和Servlet.class
        @ConditionalOnClass({ Servlet.class, Tomcat.class }) // 當前Spring容器中不存在EmbeddedServletContainerFactory類型的實例
        @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT) public static class EmbeddedTomcat {
    
            @Bean
            public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
                // 上述條件註解成立的話就會構造TomcatEmbeddedServletContainerFactory這個EmbeddedServletContainerFactory
                return new TomcatEmbeddedServletContainerFactory();
            }
        }
        
        @Configuration
        @ConditionalOnClass({ Servlet.class, Server.class, Loader.class,
                WebAppContext.class })
        @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
        public static class EmbeddedJetty {
    
            @Bean
            public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() {
                return new JettyEmbeddedServletContainerFactory();
            }
    
        }
        
        @Configuration
        @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
        @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
        public static class EmbeddedUndertow {
    
            @Bean
            public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory() {
                return new UndertowEmbeddedServletContainerFactory();
            }
    
        }
        
        //other code...
    }

    在這個自動配置類中配置了三個容器工廠的Bean,分別是:

    • TomcatEmbeddedServletContainerFactory

    • JettyEmbeddedServletContainerFactory

    • UndertowEmbeddedServletContainerFactory

    這裏以大家熟悉的Tomcat為例,首先Spring Boot會判斷當前環境中是否引入了Servlet和Tomcat依賴,並且當前容器中沒有自定義的
    EmbeddedServletContainerFactory的情況下,則創建Tomcat容器工廠。其他Servlet容器工廠也是同樣的道理。

    EmbeddedServletContainerFactory

    • 嵌入式Servlet容器工廠
    public interface EmbeddedServletContainerFactory {
    
        EmbeddedServletContainer getEmbeddedServletContainer( ServletContextInitializer... initializers);
    }

    內部只有一個方法,用於獲取嵌入式的Servlet容器。

    該工廠接口主要有三個實現類,分別對應三種嵌入式Servlet容器的工廠類,如圖所示:

    TomcatEmbeddedServletContainerFactory

    以Tomcat容器工廠TomcatEmbeddedServletContainerFactory類為例:

    public class TomcatEmbeddedServletContainerFactory extends AbstractEmbeddedServletContainerFactory implements ResourceLoaderAware {
        
        //other code...
        
        @Override
        public EmbeddedServletContainer getEmbeddedServletContainer( ServletContextInitializer... initializers) {
            //創建一個Tomcat
            Tomcat tomcat = new Tomcat(); //配置Tomcat的基本環節
            File baseDir = (this.baseDirectory != null ? this.baseDirectory: createTempDir("tomcat"));
            tomcat.setBaseDir(baseDir.getAbsolutePath());
            Connector connector = new Connector(this.protocol);
           tomcat.getService().addConnector(connector);
            customizeConnector(connector);
          tomcat.setConnector(connector); tomcat.getHost().setAutoDeploy(false);
            configureEngine(tomcat.getEngine());
            for (Connector additionalConnector : this.additionalTomcatConnectors) {
                tomcat.getService().addConnector(additionalConnector);
            }
            prepareContext(tomcat.getHost(), initializers);
            
            //包裝tomcat對象,返回一個嵌入式Tomcat容器,內部會啟動該tomcat容器
            return getTomcatEmbeddedServletContainer(tomcat);
        }
    }

    首先會創建一個Tomcat的對象,並設置一些屬性配置,最後調用getTomcatEmbeddedServletContainer(tomcat)方法,內部會啟動tomcat,我們來看看:

    protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
        Tomcat tomcat) {
        return new TomcatEmbeddedServletContainer(tomcat, getPort() >= 0);
    }

    該函數很簡單,就是來創建Tomcat容器並返回。看看TomcatEmbeddedServletContainer類:

    public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer {
    
        public TomcatEmbeddedServletContainer(Tomcat tomcat, boolean autoStart) {
            Assert.notNull(tomcat, "Tomcat Server must not be null");
            this.tomcat = tomcat;
            this.autoStart = autoStart;
            
            //初始化嵌入式Tomcat容器,並啟動Tomcat
     initialize();
        }
        
        private void initialize() throws EmbeddedServletContainerException {
            TomcatEmbeddedServletContainer.logger
                    .info("Tomcat initialized with port(s): " + getPortsDescription(false));
            synchronized (this.monitor) {
                try {
                    addInstanceIdToEngineName();
                    try {
                        final Context context = findContext();
                        context.addLifecycleListener(new LifecycleListener() {
    
                            @Override
                            public void lifecycleEvent(LifecycleEvent event) {
                                if (context.equals(event.getSource())
                                        && Lifecycle.START_EVENT.equals(event.getType())) {
                                    // Remove service connectors so that protocol
                                    // binding doesn't happen when the service is
                                    // started.
                                    removeServiceConnectors();
                                }
                            }
    
                        });
    
                        // Start the server to trigger initialization listeners
                        //啟動tomcat
                        this.tomcat.start(); // We can re-throw failure exception directly in the main thread
                        rethrowDeferredStartupExceptions();
    
                        try {
                            ContextBindings.bindClassLoader(context, getNamingToken(context),
                                    getClass().getClassLoader());
                        }
                        catch (NamingException ex) {
                            // Naming is not enabled. Continue
                        }
    
                        // Unlike Jetty, all Tomcat threads are daemon threads. We create a
                        // blocking non-daemon to stop immediate shutdown
                        startDaemonAwaitThread();
                    }
                    catch (Exception ex) {
                        containerCounter.decrementAndGet();
                        throw ex;
                    }
                }
                catch (Exception ex) {
                    stopSilently();
                    throw new EmbeddedServletContainerException(
                            "Unable to start embedded Tomcat", ex);
                }
            }
        }
    }

    到這裏就啟動了嵌入式的Servlet容器,其他容器類似。

    Servlet容器啟動原理

    SpringBoot啟動過程

    我們回顧一下前面講解的SpringBoot啟動過程,也就是run方法:

    public ConfigurableApplicationContext run(String... args) {
        // 計時工具
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
    
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    
        configureHeadlessProperty();
    
        // 第一步:獲取並啟動監聽器
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();
        
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    
            // 第二步:根據SpringApplicationRunListeners以及參數來準備環境
            ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
            configureIgnoreBeanInfo(environment);
    
            // 準備Banner打印器 - 就是啟動Spring Boot的時候打印在console上的ASCII藝術字體
            Banner printedBanner = printBanner(environment);
    
            // 第三步:創建Spring容器
            context = createApplicationContext();
    
            exceptionReporters = getSpringFactoriesInstances(
                    SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
    
            // 第四步:Spring容器前置處理
            prepareContext(context, environment, listeners, applicationArguments,printedBanner);
    
            // 第五步:刷新容器
     refreshContext(context);
    
         // 第六步:Spring容器後置處理
            afterRefresh(context, applicationArguments);
    
          // 第七步:發出結束執行的事件
            listeners.started(context);
            // 第八步:執行Runners
            this.callRunners(context, applicationArguments);
            stopWatch.stop();
            // 返回容器
            return context;
        }
        catch (Throwable ex) {
            handleRunFailure(context, listeners, exceptionReporters, ex);
            throw new IllegalStateException(ex);
        }
    }

    我們回顧一下第三步:創建Spring容器

    public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
                + "annotation.AnnotationConfigApplicationContext";
    
    public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework."
                + "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";
    
    protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                //根據應用環境,創建不同的IOC容器
                contextClass = Class.forName(this.webEnvironment ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
            }
        }
        return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
    }

    創建IOC容器,如果是web應用,則創建
    AnnotationConfigEmbeddedWebApplicationContext的IOC容器;如果不是,則創建AnnotationConfigApplicationContext的IOC容器;很明顯我們創建的容器是AnnotationConfigEmbeddedWebApplicationContext
    接着我們來看看
    第五步,刷新容器
    refreshContext(context);

    private void refreshContext(ConfigurableApplicationContext context) {
        refresh(context);
    }
    
    protected void refresh(ApplicationContext applicationContext) {
        Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
        //調用容器的refresh()方法刷新容器
     ((AbstractApplicationContext) applicationContext).refresh();
    }

    容器刷新過程

    調用抽象父類AbstractApplicationContext的refresh()方法;

    AbstractApplicationContext

     1 public void refresh() throws BeansException, IllegalStateException {
     2     synchronized (this.startupShutdownMonitor) {
     3         /**
     4          * 刷新上下文環境
     5          */
     6         prepareRefresh();
     7 
     8         /**
     9          * 初始化BeanFactory,解析XML,相當於之前的XmlBeanFactory的操作,
    10          */
    11         ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    12 
    13         /**
    14          * 為上下文準備BeanFactory,即對BeanFactory的各種功能進行填充,如常用的註解@Autowired @Qualifier等
    15          * 添加ApplicationContextAwareProcessor處理器
    16          * 在依賴注入忽略實現*Aware的接口,如EnvironmentAware、ApplicationEventPublisherAware等
    17          * 註冊依賴,如一個bean的屬性中含有ApplicationEventPublisher(beanFactory),則會將beanFactory的實例注入進去
    18          */
    19         prepareBeanFactory(beanFactory);
    20 
    21         try {
    22             /**
    23              * 提供子類覆蓋的額外處理,即子類處理自定義的BeanFactoryPostProcess
    24              */
    25             postProcessBeanFactory(beanFactory);
    26 
    27             /**
    28              * 激活各種BeanFactory處理器,包括BeanDefinitionRegistryBeanFactoryPostProcessor和普通的BeanFactoryPostProcessor
    29              * 執行對應的postProcessBeanDefinitionRegistry方法 和  postProcessBeanFactory方法
    30              */
    31             invokeBeanFactoryPostProcessors(beanFactory);
    32 
    33             /**
    34              * 註冊攔截Bean創建的Bean處理器,即註冊BeanPostProcessor,不是BeanFactoryPostProcessor,注意兩者的區別
    35              * 注意,這裏僅僅是註冊,並不會執行對應的方法,將在bean的實例化時執行對應的方法
    36              */
    37             registerBeanPostProcessors(beanFactory);
    38 
    39             /**
    40              * 初始化上下文中的資源文件,如國際化文件的處理等
    41              */
    42             initMessageSource();
    43 
    44             /**
    45              * 初始化上下文事件廣播器,並放入applicatioEventMulticaster,如ApplicationEventPublisher
    46              */
    47             initApplicationEventMulticaster();
    48 
    49             /**
    50  * 給子類擴展初始化其他Bean 51              */
    52  onRefresh(); 53 
    54             /**
    55              * 在所有bean中查找listener bean,然後註冊到廣播器中
    56              */
    57             registerListeners();
    58 
    59             /**
    60              * 設置轉換器
    61              * 註冊一個默認的屬性值解析器
    62              * 凍結所有的bean定義,說明註冊的bean定義將不能被修改或進一步的處理
    63              * 初始化剩餘的非惰性的bean,即初始化非延遲加載的bean
    64              */
    65             finishBeanFactoryInitialization(beanFactory);
    66 
    67             /**
    68              * 通過spring的事件發布機制發布ContextRefreshedEvent事件,以保證對應的監聽器做進一步的處理
    69              * 即對那種在spring啟動后需要處理的一些類,這些類實現了ApplicationListener<ContextRefreshedEvent>,
    70              * 這裏就是要觸發這些類的執行(執行onApplicationEvent方法)
    71              * spring的內置Event有ContextClosedEvent、ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、RequestHandleEvent
    72              * 完成初始化,通知生命周期處理器lifeCycleProcessor刷新過程,同時發出ContextRefreshEvent通知其他人
    73              */
    74             finishRefresh();
    75         }
    76 
    77         finally {
    78     
    79             resetCommonCaches();
    80         }
    81     }
    82 }

    我們看第52行的方法:

    protected void onRefresh() throws BeansException {
    
    }

    很明顯抽象父類AbstractApplicationContext中的onRefresh是一個空方法,並且使用protected修飾,也就是其子類可以重寫onRefresh方法,那我們看看其子類AnnotationConfigEmbeddedWebApplicationContext中的onRefresh方法是如何重寫的,AnnotationConfigEmbeddedWebApplicationContext又繼承EmbeddedWebApplicationContext,如下:

    public class AnnotationConfigEmbeddedWebApplicationContext extends EmbeddedWebApplicationContext {

    那我們看看其父類EmbeddedWebApplicationContext 是如何重寫onRefresh方法的:

    EmbeddedWebApplicationContext

    @Override
    protected void onRefresh() {
        super.onRefresh();
        try {
            //核心方法:會獲取嵌入式的Servlet容器工廠,並通過工廠來獲取Servlet容器
     createEmbeddedServletContainer();
        }
        catch (Throwable ex) {
            throw new ApplicationContextException("Unable to start embedded container", ex);
        }
    }

    在createEmbeddedServletContainer方法中會獲取嵌入式的Servlet容器工廠,並通過工廠來獲取Servlet容器:

     1 private void createEmbeddedServletContainer() {
     2     EmbeddedServletContainer localContainer = this.embeddedServletContainer;
     3     ServletContext localServletContext = getServletContext();
     4     if (localContainer == null && localServletContext == null) {
     5         //先獲取嵌入式Servlet容器工廠
     6         EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();  7         //根據容器工廠來獲取對應的嵌入式Servlet容器
     8         this.embeddedServletContainer = containerFactory.getEmbeddedServletContainer(getSelfInitializer());  9     }
    10     else if (localServletContext != null) {
    11         try {
    12             getSelfInitializer().onStartup(localServletContext);
    13         }
    14         catch (ServletException ex) {
    15             throw new ApplicationContextException("Cannot initialize servlet context",ex);
    16         }
    17     }
    18     initPropertySources();
    19 }

    關鍵代碼在第6和第8行,先獲取Servlet容器工廠,然後根據容器工廠來獲取對應的嵌入式Servlet容器

    獲取Servlet容器工廠

    protected EmbeddedServletContainerFactory getEmbeddedServletContainerFactory() {
        //從Spring的IOC容器中獲取EmbeddedServletContainerFactory.class類型的Bean
        String[] beanNames = getBeanFactory().getBeanNamesForType(EmbeddedServletContainerFactory.class); //調用getBean實例化EmbeddedServletContainerFactory.class
        return getBeanFactory().getBean(beanNames[0], EmbeddedServletContainerFactory.class);
    }

    我們看到先從Spring的IOC容器中獲取EmbeddedServletContainerFactory.class類型的Bean,然後調用getBean實例化EmbeddedServletContainerFactory.class,大家還記得我們第一節Servlet容器自動配置類EmbeddedServletContainerAutoConfiguration中注入Spring容器的對象是什麼嗎?當我們引入spring-boot-starter-web這個啟動器后,會注入TomcatEmbeddedServletContainerFactory這個對象到Spring容器中,所以這裏獲取到的Servlet容器工廠是TomcatEmbeddedServletContainerFactory,然後調用

    TomcatEmbeddedServletContainerFactory的getEmbeddedServletContainer方法獲取Servlet容器,並且啟動Tomcat,大家可以看看文章開頭的getEmbeddedServletContainer方法。

    大家看一下第8行代碼獲取Servlet容器方法的參數getSelfInitializer(),這是個啥?我們點進去看看

    private ServletContextInitializer getSelfInitializer() {
        //創建一個ServletContextInitializer對象,並重寫onStartup方法,很明顯是一個回調方法
        return new ServletContextInitializer() { public void onStartup(ServletContext servletContext) throws ServletException { EmbeddedWebApplicationContext.this.selfInitialize(servletContext); } };
    }

    創建一個ServletContextInitializer對象,並重寫onStartup方法,很明顯是一個回調方法,這裏給大家留一點疑問:

    • ServletContextInitializer對象創建過程是怎樣的?
    • onStartup是何時調用的?
    • onStartup方法的作用是什麼?

    ServletContextInitializer是 Servlet 容器初始化的時候,提供的初始化接口。這裏涉及到Servlet、Filter實例的註冊,我們留在下一篇具體講

     

     

     

     

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

    ※專營大陸快遞台灣服務

    台灣快遞大陸的貨運公司有哪些呢?

  • 詳解JavaScript錯誤捕獲和上報流程

    詳解JavaScript錯誤捕獲和上報流程

     

     

     

     

    怎麼捕獲錯誤並且處理,是一門語言必備的知識。在JavaScript中也是如此。

    那怎麼捕獲錯誤呢?初看好像很簡單,try-catch就可以了嘛!但是有的時候我們發現情況卻繁多複雜。

    • Q1: 同步可以try-catch,但一個異步回調,比如setTimeOut里的函數還可以try-catch嗎?

    • Q2: Promise的錯誤捕獲怎麼做?

    • Q3: async/await怎麼捕獲錯誤?

    • Q4: 我能夠在全局環境下捕獲錯誤並且處理嗎?

    • Q5: React16有什麼新的錯誤捕獲方式嗎?

    • Q6: 捕獲之後怎麼上報和處理?

     

    問題有點多,我們一個一個來。

     

    Q1. 同步代碼里的錯誤捕獲方式

    在同步代碼里,我們是最簡單的,只要try-catch就完了 

    function test1 () {
      try {
        throw Error ('callback err');
      } catch (error) {
        console.log ('test1:catch err successfully');
      }
    }
    test1();

    輸出結果如下,顯然是正常的

    Q2. 普通的異步回調里的錯誤捕獲方式(Promise時代以前)

    上面的問題來了,我們還能通過直接的try-catch在異步回調外部捕獲錯誤嗎?我們試一試 

    // 嘗試在異步回調外部捕獲錯誤的結果
    function test2 () {
      try {
        setTimeout (function () {
          throw Error ('callback err');
        });
      } catch (error) {
        console.log ('test2:catch err successfully');
      }
    }
    test2(); 

    輸出

    注意這裏的Uncaught Error的文本,它告訴我們錯誤沒有被成功捕捉。

    為什麼呢? 因為try-catch的是屬於同步代碼,它執行的時候,setTimeOut內部的的匿名函數還沒有執行呢。而內部的那個匿名函數執行的時候,try-catch早就執行完了。( error的內心想法:哈哈,只要我跑的夠慢,try-catch還是追不上我!)

    但是我們簡單想一想,誒我們把try-catch寫到函數裏面不就完事了嘛!

     

     

    function test2_1 () {
      setTimeout (function () {
        try {
          throw Error ('callback err');
        } catch (error) {
          console.log ('test2_1:catch err successfully');
        }
      });
    }
    test2_1();

    輸出結果如下,告訴我們這方法可行

     

    總結下Promise時代以前,異步回調中捕獲和處理錯誤的方法

    • 在異步回調內部編寫try-catch去捕獲和處理,不要在外部哦

    • 很多異步操作會開放error事件,我們根據事件去操作就可以了

    Q3. Promise里的錯誤捕獲方式

    可通過Promise.catch方法捕獲

    function test3 () {
      new Promise ((resolve, reject) => {
        throw Error ('promise error');
      }).catch (err => {
        console.log ('promise error');
      });
    }

    輸出結果

    >> reject方法調用和throw Error都可以通過Promise.catch方法捕獲

    function test4 () {
      new Promise ((resolve, reject) => {
        reject ('promise reject error');
      }).catch (err => {
        console.log (err);
      });
    } 

    輸出結果

     

    >> then方法中的失敗回調和Promise.catch的關係

    • 如果前面的then方法沒寫失敗回調,失敗時後面的catch是會被調用的

    • 如果前面的then方法寫了失敗回調,又沒拋出,那麼後面的catch就不會被調用了

    // then方法沒寫失敗回調
    function test5 () {
      new Promise ((resolve, reject) => {
        throw Error ('promise error');
      })
        .then (success => {})
        .catch (err => {
          console.log ('the error has not been swallowed up');
        });
    }
    // then方法寫了失敗回調
    function test5 () {
      new Promise ((resolve, reject) => {
        throw Error ('promise error');
      })
        .then (success => {},err => {})
        .catch (err => {
          console.log ('the error has not been swallowed up');
        });
    }

    輸出分別為

    1.the error has not been swallowed up
    2.無輸出

    Q4.async/await里的錯誤捕獲方式

    對於async/await這種類型的異步,我們可以通過try-catch去解決

    async function test6 () {
      try {
        await getErrorP ();
      } catch (error) {
        console.log ('async/await error with throw error');
      }
    }
     
    function getErrorP () {
      return new Promise ((resolve, reject) => {
        throw Error ('promise error');
      });
    }
    test6();
    
    

    輸出結果如下

     

    >> 如果被await修飾的Promise因為reject調用而變化,它也是能被try-catch的

    (我已經證明了這一點,但是這裏位置不夠,我寫不下了)

    Q5.在全局環境下如何監聽錯誤

    window.onerror可以監聽全局錯誤,但是很顯然錯誤還是會拋出

    window.onerror = function (err) {
      console.log ('global error');
    };
    throw Error ('global error');

     

    輸出如下

     

    Q6.在React16以上如何監聽錯誤

    >> componentDidCatch和getDerivedStateFromError鈎子函數

    class Bar extends React.Component {
      // 監聽組件錯誤
      componentDidCatch(error, info) {
        this.setState({ error, info });
      }
      // 更新 state 使下一次渲染能夠显示降級后的 UI
      static getDerivedStateFromError(error) {
        return { hasError: true };
      }
      render() {
      }
    }

     

     

    有錯誤,那肯定要上報啊!不上報就發現不了Bug這個樣子。Sentry這位老哥就是個人才,日誌記錄又好看,每次見面就像回家一樣

     

     

    Sentry簡單介紹

    Sentry provides open-source and hosted error monitoring that helps all software
    teams discover, triage, and prioritize errors in real-time.
    One million developers at over fifty thousand companies already ship
    better software faster with Sentry. Won’t you join them?
    —— Sentry官網

     

    Sentry是一個日誌上報系統,Sentry 是一個實時的日誌記錄和匯總處理的平台。專註於錯誤監控,發現和數據處理,可以讓我們不再依賴於用戶反饋才能發現和解決線上bug。讓我們簡單看一下Sentry支持哪些語言和平台吧

     

    在JavaScript領域,Sentry的支持也可以說是面面俱到

     

    參考鏈接
    https://docs.sentry.io/platforms/ 

    Sentry的功能簡單說就是,你在代碼中catch錯誤,然後調用Sentry的方法,然後Sentry就會自動幫你分析和整理錯誤日誌,例如下面這張圖截取自Sentry的網站中

     

    在JavaScript中使用Sentry 

    1.首先呢,你當然要註冊Sentry的賬號

    這個時候Sentry會自動給你分配一個唯一標示,這個標示在Sentry里叫做 dsn

    2. 安卓模塊並使用基礎功能

    安裝@sentry/browser 

    npm install @sentry/browser

     

    在項目中初始化並使用

    import * as Sentry from '@sentry/browser';
     
    Sentry.init ({
      dsn: 'xxxx',
    });
     
    try {
      throw Error ('我是一個error');
    } catch (err) {
        // 捕捉錯誤
      Sentry.captureException (err);
    }

    3.上傳sourceMap以方便在線上平台閱讀出錯的源碼

     

    // 安裝
    $ npm install --save-dev @sentry/webpack-plugin
    $ yarn add --dev @sentry/webpack-plugin
     
    // 配置webpack
    const SentryWebpackPlugin = require('@sentry/webpack-plugin');
    module.exports = {
      // other configuration
      plugins: [
        new SentryWebpackPlugin({
          include: '.',
          ignoreFile: '.sentrycliignore',
          ignore: ['node_modules', 'webpack.config.js'],
          configFile: 'sentry.properties'
        })
      ]
    }; 

    4. 為什麼不是raven.js?

     

    // 已經廢棄,雖然你還是可以用
    var Raven = require('raven-js');
    Raven
      .config('xxxxxxxxxxx_dsn')
      .install();

     

    Sentry的核心功能總結

    捕獲錯誤

    try { aFunctionThatMightFail(); } catch (err) { Sentry.captureException(err); }

     

    設置該錯誤發生的用戶信息

    下面每個選項都是可選的,但必須 存在一個選項 才能使Sentry SDK捕獲用戶: id 

    Sentry.setUser({
        id:"penghuwan12314"
      email: "penghuwan@example.com",
      username:"penghuwan",
      ip_addressZ:'xxx.xxx.xxx.xxx'
      });

     

    設置額外數據

    Sentry.setExtra("character_name", "Mighty Fighter");
    設置作用域 
    Sentry.withScope(function(scope) {
        // 下面的set的效果只存在於函數的作用域內
      scope.setFingerprint(['Database Connection Error']);
      scope.setUser(someUser);
      Sentry.captureException(err);
    });
    // 在這裏,上面的setUser的設置效果會消失

     

    設置錯誤的分組

    整理日誌信息,避免過度冗餘 

    Sentry.configureScope(function(scope) {
      scope.setFingerprint(['my-view-function']);
    });

     

    設置錯誤的級別

    在閱讀日誌時可以確定各個bug的緊急度,確定排查的優先書序

    Sentry.captureMessage('this is a debug message', 'debug');
    //fatal,error,warning,info,debug五個值
    // fatal最嚴重,debug最輕

     

    自動記錄某些事件

    例如下面的方法,會在每次屏幕調整時完成上報 

    window.addEventListener('resize', function(event){
      Sentry.addBreadcrumb({
        category: 'ui',
        message: 'New window size:' + window.innerWidth + 'x' + window.innerHeight,
        level: 'info'
      });
    })

    Sentry實踐的運用

    根據環境設置不同的dsn

    let dsn;
      if (env === 'test') {
        dsn = '測試環境的dsn';
      } else {
        dsn =
          '正式環境的dsn';
      }
     
    Sentry.init ({
      dsn
    });

     

     

     

     

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

    ※專營大陸快遞台灣服務

    台灣快遞大陸的貨運公司有哪些呢?