標籤: 台北網頁設計公司

  • 區塊鏈和分佈式数字賬本正火?如何在7天內快速掌握這些必備的知識

    前言

    讀研期間,幾次被問到:“什麼是區塊鏈?”“我怎麼學習區塊鏈”。甚至跟“上至九十九”(爺爺奶奶),“下至剛會走的”(小學生的弟弟妹妹)解釋過我的研究內容。因此想總結一份7天的學習計劃(大量資料警告),讓那些對區塊鏈感興趣的人快速入門。區塊鏈技術的被運用到各行各業,尤其是各界(商界和政府)都對區塊鏈技術創新高度重視。本文總結了諸多入門區塊鏈的主要資源,博客、書籍和視頻,希望幫你用7天時間獲取必要的區塊鏈基礎知識。

    作者宇宙之一粟,轉載請先聲明出處
    公眾號宇宙之一粟,關注公眾號獲取更多相關資源

    介紹

    區塊鏈,剛開始聽起來會很新穎,因為是一個英文組合詞彙( Block + Chain )。區塊:存放數據的載體(想象成箱子),鏈:把這些箱子首尾相連(類似數據結構中的單鏈表)。

    區塊鏈的本質:一個去中心化的分佈式賬本數據庫。其本身是一串使用密碼學相關聯所產生的數據塊,每一個數據塊中包含了多次比特幣網絡交易有效確認的信息。

    區塊鏈技術從比特幣中發展而來,是比特幣的底層技術,和比特幣是相伴相生的關係。目前這項技術已經不僅僅限於比特幣——青出於藍而勝於藍。各行各業開始利用這項技術尋求突破。

    2019年6月,Facebook宣布了其加密貨幣項目——。

    我們國家央行也將發行自己的数字貨幣——( Digital Currency Electronic Payment ),也採用了區塊鏈技術,目前仍處於研究測試過程中。

    2019年10月24日下午,。一時間區塊鏈技術又衝上了風口。

    因此,我們有必要對區塊鏈技術做一個基本的了解。

    如何獲得知識

    如果想快速學習區塊鏈知識:收聽音頻課,閱讀書籍,看論文或在線文章,學習白皮書以及參与區塊鏈的相關學習計劃或在線課程。其他針對想從事更加專業的學習人員——參加編程或智能合約編碼課程。

    應該學習到什麼程度

    在區塊鏈領域工作或打算工作的每個人至少應該能夠理解並編寫一個非常簡單的智能合約,例如以程序語言Solidity通過幾行代碼實現的託管過程。儘管其他人會說,在學習區塊鏈和DLT時應該把重點放在另一個方面,但在我們看來,這是一條清晰的聲明。

    在我們看來,理解簡單的智能合約(例如5-10行代碼)的需求不僅對於信息系統領域的員工很重要,而且在商業,經濟和法律領域也很重要。但是,關於如何配置節點的更深入的技術知識通常不是緊要的,但以後可以獲取。

    任何尋求參与區塊鏈技術並考慮以下步驟的人都可能擁有光榮的職業生涯。我們認為,區塊鏈技術及其日益重要的潛力有可能對感興趣的人的職業道路產生积極影響,因為在這個動態且快速增長的細分市場中,未來幾年甚至數十年內還有很多工作要做。這樣,可以將由於数字化和自動化程度提高而失業(例如在金融部門)的風險降到最低。

    但是感興趣的人如何獲得足夠的知識呢?存在哪些研究區塊鏈技術的機會?本文總結了區塊鏈教育的一些不同策略。我們基本上設計了一個特定的程序,以在大約7個工作日內實現“區塊鏈入門”。

    一、購買或轉讓加密貨幣

    所需時間:0.5天

    除了獲得一些理論知識之外,理解此技術非常重要的是您在操作上“弄髒手”。 它的工作方式如下,為感興趣的人提供有關什麼是區塊鏈技術的最重要學習。

    因此,開始的最佳練習是:購買加密貨幣(例如比特幣)並在全球範圍內進行轉移。 如下所示,請找到分步指南:

    • 在例如或比特派上開設一個帳戶;其他選擇:Bitstamp,Bitrex,Binance。
    • 購買比特幣或者購買以太幣。請注意,萬一使用公鑰或私鑰出錯,這筆錢將不可避免地丟失。因此,遵循此步驟當然是每個人自己的決定。
    • 開設第二個帳戶(上面的示例)。
    • 從第二個帳戶獲取一個錢包地址,該地址類似於以太坊的0xd42899dcC146d4788649e6aa5B09f129fC269127。
    • 第一步,將您購買的一部分以太幣或比特幣轉移到該地址。請注意,以太幣和比特幣的地址不同,因此請謹慎行事,切勿混淆。
    • 現在,您可以看到可以在幾秒鐘(以太)或幾分鐘(比特幣)內將價值發送到世界各地。
    • 一個更高級的步驟,但對理解區塊鏈非常有幫助:將插件添加到Google Chrome互聯網瀏覽器中,以直接訪問以太坊網絡並將您的以太幣存儲在您自己的錢包中。之後,開設一個帳戶並生成您自己的錢包地址。然後,將您的一些以太幣從之前的步驟轉移到這個新生成的錢包中。

    二、音頻課

    所需時間:1.5天

    如果你喜歡聽音頻課,選擇一個合適的音頻可以快速入門:

    • Kryptoshow: Julian Hosp博士很好地介紹了區塊鏈技術和加密貨幣這一主題。播客還涵蓋了高級方面,例如在密碼學和編程方面。在訪問播客。
    • BTC Echo:此播客面向更高級的受眾,重點關注比特幣和加密資產。在訪問播客。
    • 從0到1全面學透區塊鏈音頻教程,在學習

    三、閱讀重要的白皮書

    所需時間:1天

    為了了解區塊鏈技術的起源和概念,有必要研究兩種最重要的加密貨幣比特幣和以太坊的白皮書。其他補充Filecoin白皮書。

    最重要的論文:

    • 比特幣白皮書:中本聰(2008):比特幣:點對點电子現金系統,。
    • 以太坊白皮書:Buterin,V.(2013):以太坊白皮書:下一代智能合約和去中心化應用平台, 。
    • Filecoin白皮書: Protocol Labs(2017):Filecoin: A Decentralized Storage Network,
      (待完善)

    四、觀看視頻

    所需時間:2天

    在通過閱讀有關比特幣和以太坊的原始著作從“科學”角度看待區塊鏈技術之後,您可以使用視頻進一步熟悉該技術並更深入地了解該技術的不同組成部分。我們推薦以下視頻:

    最重要的視頻:

    五、更多文章和網站

    所需時間:1天

    通過遵循建議的步驟,您現在應該已經對技術有了基本的高級知識。現在,是時候更深入地研究區塊鏈技術了。我們建議閱讀以下文章:

    文章或文檔:

    需要了解的項目的網站:

    • 以太坊:
    • EOS:
    • NULS:
    • FISCO BCOS:

    六、更廣泛概述的書籍

    所需時間:2天閱讀

    其他書籍:

    • 中文版(第2版)
    • Andreas M. Antonopoulos / Gavin Wood(2018年):

    Ps: 關注公眾號 宇宙之一粟 回復關鍵字 “區塊鏈” 獲取电子版

    七、認識志同道合之人

    所需時間:參加2次聚會(主要是免費)

    下一步,我們建議您進入區塊鏈社區,並與區塊鏈專家進行首次討論。

    科學論文

    所需時間:0天,因為這是可選的

    如果您打算從科學的角度更多地探討區塊鏈的話題,那麼就有可能研究有關區塊鏈技術的論文。我們將進一步擴大此列表。

    文件:

    • Eyal I , Gencer A E , Sirer E G , et al.
    • Gilad, Yossi & Hemo, Rotem & Micali, Silvio & Vlachos, Georgios & Zeldovich, Nickolai. (2017).
    • Li C , Li P , Zhou D , et al.
    • Yin M , Malkhi D , Reiter M K , et al.
    • Wang, Jiaping & Wang, Hao. (2019).
    • 更多區塊鏈相關論文

    現在?

    重要的是“閱讀”有關區塊鏈和DLT中的教育如何工作的信息。歸根結底,只有一件重要的事情:那就去做。不要只是想,而要行動。

    歡迎評論給我,無論您是否遵循我的建議–並向我提供反饋。如果您還有其他想法,請告訴我們如何學習和使用區塊鏈。如果您使用其他來源來獲取或增加您的區塊鏈知識,那麼如果您能與我分享這些知識,我將非常感激。

    備註

    在Medium看到Education in Blockchain and DLT: How to Acquire the Necessary Knowledge with a Workload of 10 Working Days的一篇博客,但是有很多網站和學習資料使我們不能訪問的。基於此博客,製作了方便我們快速學習並掌握區塊鏈必備知識的這篇文章。

    如果您喜歡本文,將其轉發或分享(附上原文出處),我將非常高興並表示感謝。
    也歡迎關注我的微信公眾號:宇宙之一粟,分享更多資料,與你讀書學習精進。

    參考博客: Authors: Philipp Sandner, Jonas Groß

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

    【其他文章推薦】

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

  • JVM原理速記複習Java虛擬機總結思維導圖面試必備

    JVM原理速記複習Java虛擬機總結思維導圖面試必備

    良心製作,右鍵另存為保存

    喜歡可以點個贊哦

    Java虛擬機

    一、運行時數據區域

    線程私有

    • 程序計數器

      • 記錄正在執行的虛擬機字節碼指令的地址(如果正在執行的是Native方法則為空),是唯一一個沒有規定OOM(OutOfMemoryError)的區域。
    • Java虛擬機棧

      • 每個Java方法在執行的同時會創建一個棧楨用於存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。從方法調用直到執行完成的過程,對應着一個棧楨在Java虛擬機棧中入棧和出棧的過程。(局部變量包含基本數據類型、對象引用reference和returnAddress類型)
    • 本地方法棧

      • 本地方法棧與Java虛擬機棧類似,它們之間的區別只不過是本地方法棧為Native方法服務。

    線程公有

    • Java堆(GC區)(Java Head)

      • 幾乎所有的對象實例都在這裏分配內存,是垃圾收集器管理的主要區域。分為新生代和老年代。對於新生代又分為Eden空間、From Survivor空間、To Survivor空間。
    • JDK1.7 方法區(永久代)

      • 用於存放已被加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據。
        對這塊區域進行垃圾回收的主要目的是對常量池的回收和對類的卸載,但是一般難以實現。
        HotSpot虛擬機把它當做永久代來進行垃圾回收。但很難確定永久代的大小,因為它受到很多因素的影響,並且每次Full GC之後永久代的大小都會改變,所以經常拋出OOM異常。
        從JDK1.8開始,移除永久代,並把方法區移至元空間。
      • 運行時常量池

        • 是方法區的一部分
          Class文件中的常量池(編譯器生成的字面量和符號引用)會在類加載后被放入這個區域。
          允許動態生成,例如String類的intern()
    • JDK1.8 元空間

      • 原本存在方法區(永久代)的數據,一部分移到了Java堆裏面,一部分移到了本地內存裏面(即元空間)。元空間存儲類的元信息,靜態變量和常量池等放入堆中。
    • 直接內存

      • 在NIO中,會使用Native函數庫直接分配堆外內存。

    二、HotSpot虛擬機

    對象的創建

    • 當虛擬機遇到一條new指令時
    1. 檢查參數能否在常量池中找到符號引用,並檢查這個符號引用代表的類是否已經被加載、解析和初始過,沒有的話先執行相應的類加載過程。
    2. 在類加載檢查通過之後,接下來虛擬機將為新生對象分配內存。
    3. 內存分配完成之後,虛擬機需要將分配到的內存空間都初始化為零值(不包括對象頭)。
    4. 對對象頭進行必要的設置。
    5. 執行構造方法按照程序員的意願進行初始化。

    對象的內存布局

      1. 對象頭
        1. 第一部分用於存儲對象自身的運行時數據,如哈希碼、GC分代年齡、鎖狀態標識、線程持有的鎖、偏向線程ID、偏向實現戳等。
        1. 第二部分是類型指針,即對象指向它的類元數據的指針(如果使用直接對象指針訪問),虛擬機通過這個指針來確定這個對象是哪個類的實例。
        1. 如果對象是一個Java數組的話,還需要第三部分記錄數據長度的數據。
      1. 實例數據
      • 是對象真正存儲的有效信息,也就是在代碼中定義的各種類型的字段內容。
      1. 對齊填充
      • 不是必然存在的,僅僅起着佔位符的作用。
        HotSpot需要對象的大小必須是8字節的整數倍。

    對象的訪問定位

    • 句柄訪問

      • 在Java堆中劃分出一塊內存作為句柄池。
        Java棧上的對象引用reference中存儲的就是對象的句柄地址,而句柄中包含了到對象實例數據的指針和到對象類型數據的指針。
        對象實例數據在Java堆中,對象類型數據在方法區(永久代)中。
        優點:在對象被移動時只會改變句柄中的實例數據指針,而對象引用本身不需要修改。
    • 直接指針訪問(HotSpot使用)

      • Java棧上的對象引用reference中存儲的就是對象的直接地址。
        在堆中的對象實例數據就需要包含到對象類型數據的指針。
        優點:節省了一次指針定位的時間開銷,速度更快。

    三、垃圾收集

    概述

    • 垃圾收集主要是針對Java堆和方法區。
      程序計數器、Java虛擬機棧個本地方法棧三個區域屬於線程私有,線程或方法結束之後就會消失,因此不需要對這三個區域進行垃圾回收。

    判斷對象是否可以被回收

    • 第一次標記(緩刑)

      • 引用計數算法

        • 給對象添加一個引用計數器,當對象增加一個引用時引用計數值++,引用失效時引用計數值–,引用計數值為0時對象可以被回收。

    但是它難以解決對象之間的相互循環引用的情況,此時這個兩個對象引用計數值為1,但是永遠無法用到這兩個對象。

    - 可達性分析算法(Java使用)
    
        - 以一系列GC Roots的對象作為起始點,從這些節點開始向下搜索,搜索所走過的路徑稱為引用鏈,當一個對象到GC Roots沒有任何引用鏈相連是,則證明此對象不可用,可以被回收。

    GC Roots對象包括

    1. 虛擬機棧(棧楨中的本地變量表)中引用的對象。
    2. 方法區中共類靜態屬性引用的對象。
    3. 方法區中常量引用的對象。
    4. 本地方法棧中JNI(即一般說的Native方法)引用的對象。
    • 第二次標記

      • 當對象沒有覆蓋finalize()方法,或者finalize()方法已經被虛擬機調用過。
        如果對象在finalize方法中重新與引用鏈上的任何一個對象建立關聯則將不會被回收。
      • finalize()

        • 任何一個對象的finalize()方法都只會被系統調用一次。
          它的出現是一個妥協,運行代價高昂,不確定性大,無法保證各個對象的調用順序。
          finalize()能做的所有工作使用try-finally或者其他方式都可以做的更好,完全可以忘記在這個函數的存在。

    方法區的回收

    • 在方法區進行垃圾回收的性價比一般比較低。
      主要回收兩部分,廢棄常量和無用的類。

    滿足無用的類三個判斷條件才僅僅代表可以進行回收,不是必然關係,可以使用-Xnoclassgc參數控制。

    1. 該類的所有實例都已經被回收,也就是Java堆中不存在該類的任何實例。
    2. 加載該類的ClassLoader已經被回收。
    3. 該類對應的java.lang.Class對象沒有在任何地方被引用,無法在任何地方通過反射訪問到該類的方法。

    引用類型

      1. 強引用
      • 使用new一個新對象的方式來創建強引用。
        只要強引用還存在,被引用的對象則永遠不會被回收。
      1. 軟引用
      • 使用SoftReference類來實現軟引用。
        用來描述一些還有用但是並非必須的對象,被引用的對象在將要發生內存溢出異常之前會被回收。
      1. 弱引用
      • 使用WeakReference類來實現弱引用。
        強度比軟引用更弱一些,被引用的對象在下一次垃圾收集時會被回收。
      1. 虛引用
      • 使用PhantomReference類來實現虛引用。
        最弱的引用關係,不會對被引用的對象生存時間構成影響,也無法通過虛引用來取得一個對象實例。
        唯一目的就是能在這個對象被收集器回收時收到一個系統通知。

    垃圾收集算法

      1. 標記 – 清除
      • 首先標記出所有需要回收的對象,在標記完成后統一回收被標記的對象並取消標記。

    不足:

    1. 效率問題,標記和清除兩個過程的效率都不高。
    2. 空間問題,標記清除之後會產生大量不連續的內存碎片,沒有連續內存容納較大對象而不得不提前觸發另一次垃圾收集。
      1. 標記 – 整理
      • 和標記 – 清除算法一樣,但標記之後讓所有存活對象都向一段移動,然後直接清理掉端邊界以外的內存。
        解決了標記 – 清除算法的空間問題,但需要移動大量對象,還是存在效率問題。
      1. 複製
      • 將可用內存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活着的對象複製到另外一塊上面,然後再把已使用多的內存空間一次清理掉。
        代價是將內存縮小為原來的一般,太高了。

    現在商業虛擬機都採用這種算法用於新生代。
    因為新生代中的對象98%都是朝生暮死,所以將內存分為一塊較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden和其中一塊Survivor空間。
    當回收時,如果另外一塊Survivor空間沒有足夠的空間存放存活下來的對象時,這些對象將直接通過分配擔保機制進入老年代。

      1. 分代收集
      • 一般把Java堆分為新生代和老年代。
        在新生代中使用複製算法,在老年代中使用標記 -清除 或者 標記 – 整理 算法來進行回收。

    HotSpot的算法實現

    • 枚舉根節點(GC Roots)

      • 目前主流Java虛擬機使用的都是準確式GC。
        GC停頓的時候,虛擬機可以通過OopMap數據結構(映射表)知道,在對象內的什麼偏移量上是什麼類型的數據,而且特定的位置記錄著棧和寄存器中哪些位置是引用。因此可以快速且準確的完成GC Roots枚舉。
    • 安全點

      • 為了節省GC的空間成本,並不會為每條指令都生成OopMap,只是在“特定的位置”記錄OopMap,這些位置稱為安全點。

    程序執行只有到達安全點時才能暫停,到達安全點有兩種方案。

    1. 搶斷式中斷(幾乎不使用)。GC時,先把所有線程中斷,如果有線程不在安全點,就恢復該線程,讓他跑到安全點。
    2. 主動式中斷(主要使用)。GC時,設置一個標誌,各個線程執行到安全點時輪詢這個標誌,發現標誌為直則掛起線程。

    但是當線程sleep或blocked時無法響應JVM的中斷請求走到安全點中斷掛起,所以引出安全區域。

    • 安全區域

      • 安全區域是指在一段代碼片段之中,引用關係不會發生變化,是擴展的安全點。

    線程進入安全區域時表示自己進入了安全區域,這個發生GC時,JVM就不需要管這個線程。
    線程離開安全區域時,檢查系統是否完成GC過程,沒有就等待可以離開安全區域的信號為止,否者繼續執行。

    垃圾收集器

    • 新生代

        1. serial收集器
        • 它是單線程收集器,只會使用一個線程進行垃圾收集工作,更重要的是它在進行垃圾收集時,必須暫停其他所有的工作線程。

    優點:對比其他單線程收集器簡單高效,對於單個CPU環境來說,沒有線程交互的開銷,因此擁有最高的單線程收集效率。

    它是Client場景下默認新生代收集器,因為在該場景下內存一般來說不會很大。

    - 2. parnew收集器
    
        - 它是Serial收集器的多線程版本,公用了相當多的代碼。

    在單CPU環境中絕對不會有比Serial收集器更好的效果,甚至在2個CPU環境中也不能百分之百超越。

    它是Server場景下默認的新生代收集器,主要因為除了Serial收集器,只用它能與CMS收集器配合使用。

    - 3. parallel scavenge收集器
    
        - “吞吐優先”收集器,與ParNew收集器差不多。

    但是其他收集器的目標是盡可能縮短垃圾收集時用戶線程停頓的時間,而它的目標是達到一個可控制的吞吐量。這裏的吞吐量指CPU用於運行用戶程序的時間佔總時間的比值。

    • 老年代

        1. serial old收集器
        • 是Serial收集器老年代版本。

    也是給Client場景下的虛擬機使用的。

    - 5. parallel old收集器
    
        - 是Parallel Scavenge收集器的老年代版本。

    在注重吞吐量已經CPU資源敏感的場合,都可以優先考慮Parallel Scavenge和Parallel Old收集器。

    - 6. cms收集器
    
        - Concurrent Mark Sweep收集器是一種以獲取最短回收停頓時間為目標的收集器。
        - 運作過程
    
            - 1. 初始標記(最短)。仍需要暫停用戶線程。只是標記一下GC Roots能直接關聯到的對象,速度很快
    1. 併發標記(耗時最長)。進行GC Roots Tracing(根搜索算法)的過程。
    2. 重新標記。修正併發標記期間因用戶程序繼續運作而導致標記產生變動的那一部分對象的標記記錄。比初始標記長但遠小於併發標記時間。
    3. 併發清除

    1 和4 兩個步驟並沒有帶上併發兩個字,即這兩個步驟仍要暫停用戶線程。

        - 優缺點
    
            - 併發收集、低停頓。
    1. CMS收集器對CPU資源非常敏感。雖然不會導致用戶線程停頓,但是佔用CPU資源會使應用程序變慢。
    2. 無法處理浮動垃圾。在併發清除階段新垃圾還會不斷的產生,所以GC時要控制“-XX:CMSinitiatingOccupancyFraction參數”預留足夠的內存空間給這些垃圾,當預留內存無法滿足程序需要時就會出現”Concurrent Mode Failure“失敗,臨時啟動Serial Old收集。
    3. 由於使用標記 – 清除算法,收集之後會產生大量空間碎片。
      1. g1收集器
      • Garbage First是一款面向服務端應用的垃圾收集器
      • 運作過程

          1. 初始標記
    1. 併發標記
    2. 最終標記
    3. 刪選標記

    五、類加載機制

    概述

    • 虛擬機把描述類的數據從Class問價加載到內存,並對數據進行校驗、轉換解析和初始化,最終形成可以被虛擬機直接使用的Java類型。
      Java應用程序的高度靈活性就是依賴運行期動態加載和動態連接實現的。

    類的生命周期

    • 加載 -> 連接(驗證 -> 準備 -> 解析) -> 初始化 -> 使用 – >卸載

    類初始化時機

    • 主動引用

      • 虛擬機規範中沒有強制約束何時進行加載,但是規定了有且只有五種情況必須對類進行初始化(加載、驗證、準備都會隨之發生)
    1. 遇到new、getstatic、putstatic、invokestatic這四條字節碼指令時沒有初始化。
    2. 反射調用時沒有初始化。
    3. 發現其父類沒有初始化則先觸發其父類的初始化。
    4. 包含psvm(mian()方法)的那個類。
    5. 動態語言支持時,REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄。
    • 被動引用

      • 除上面五種情況之外,所有引用類的方式都不會觸發初始化,稱為被動引用。
    1. 通過子類引用父類的靜態字段,不會導致子類的初始化。
    2. 通過數組定義來引用類,不會觸發此類的初始化。該過程會對數組類進行初始化,數組類是一個由虛擬機自動生成的、直接繼承Object的子類,其中包含數組的屬性和方法,用戶只能使用public的length和clone()。
    3. 常量在編譯階段會存入調用類的常量池中,本質上並沒有直接引用到定義常量的類,因此不會觸發定義常量的類的初始化。

    類加載過程

      1. 加載
        1. 通過類的全限定名來獲取定義此類的二進制字節流。
    1. 將這個字節流所代表的靜態存儲結構轉化為方法區的運行時數據結構。
    2. 在內存中生成一個代表這個類的java.lang.Class對象(HotSpot將其存放在方法區中),作為方法區這個類的各種數據的訪問入口。
      1. 驗證
      • 為了確保Class文件的字節類中包含的信息符合當前虛擬機的要求,並且不會危害虛擬機自身的安全。可以通過-Xverify:none關閉大部分類驗證。
    1. 文件格式驗證。確保輸入字節流能正確的解析並存儲於方法區,後面的3個驗證全部基於方法區的存儲結構進行,不會再操作字節流。
    2. 元數據驗證。對字節碼描述信息進行語義分析,確保其符合Java語法規範。(Java語法驗證)
    3. 字節碼驗證。最複雜,通過數據流和控制流分析,確定程序語義時合法的、符合邏輯的。可以通過參數關閉。(驗證指令跳轉範圍,類型轉換有效等)
    4. 符號引用驗證。將符號引用轉化為直接引用,發生在第三個階段——解析階段中發生。
      1. 準備
      • 類變量是被static修飾的變量,準備階段為類變量分配內存並設置零值(final直接設置初始值),使用的是方法區的內存。
      1. 解析
      • 將常量池內的符號引用替換為直接引用的過程。
        其中解析過程在某些情況下可以在初始化階段之後再開始,這是為了支持Java的動態綁定。
        解析動作主要針對類或接口、字段、類方法、接口方法、方法類型、方法句柄、和調用點限定符。
      1. 初始化
      • 初始化階段才真正執行類中定義的Java程序代碼,是執行類構造器 ()方法的過程。
        在準備階段,類變量已經給過零值,而在初始化階段,根據程序員通過程序制定的主觀計劃去初始化類變量和其他資源。

        • ()

          • 類構造器方法。是由編譯器自動收集類中的所有類變量的賦值動作和靜態語句塊中的的語句合併產生的。
    1. 不需要顯式調用父類構造器,JVM會保證在子類clinit執行之前,父類的clinit已經執行完成。
    2. 接口中不能使用靜態語句塊但仍可以有類變量的賦值操作。當沒有使用父接口中定義的變量時子接口的clinit不需要先執行父接口的clinit方法。接口的實現類也不會執行接口的clinit方法。
    3. 虛擬機會保證clinit在多線程環境中被正確的加鎖、同步。其他線性喚醒之後不會再進入clinit方法,同一個類加載器下,一個類型只會初始化一次。

       - <init>()
      
           - 對象構造器方法。Java對象被創建時才會進行實例化操作,對非靜態變量解析初始化。
    4. 會顯式的調用父類的init方法,對象實例化過程中對實例域的初始化操作全部在init方法中進行。

    類(加載) 器

    • 類與類加載器

      • 類加載器實現類的加載動作。
        類加載器和這個類本身一同確立這個類的唯一性,每個類加載器都有獨立的類命名空間。在同一個類加載器加載的情況下才會有兩個類相等。
        相等包括類的Class對象的equals()方法、isAssignableFrom()方法、isInstance()、instanceof關鍵字。
    • 類加載器分類

      • 啟動類加載器

        • 由C++語言實現,是虛擬機的一部分。負責將JAVA_HOME/lib目錄中,或者被-Xbootclasspath參數指定的路徑,但是文件名要能被虛擬機識別,名字不符合無法被啟動類加載器加載。啟動類加載器無法被Java程序直接引用。
      • 擴展類加載器

        • 由Java語言實現,負責加載JAVA_HOME/lib/ext目錄,或者被java.ext.dirs系統變量所指定的路徑中的所有類庫,開發者可以直接使用擴展類加載器。
      • 應用程序類加載器

        • 由於這個類加載器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也稱他為系統類加載器。負責加載用戶類路徑(ClassPath)上所指定的類庫,一般情況下這個就是程序中默認的類加載器。
      • 自定義類加載器

        • 由用戶自己實現。
    1. 如果不想打破雙親委派模型,那麼只需要重寫findClass方法即可。
    2. 否則就重寫整個loadClass方法。
    • 雙親委派模型

      • 雙親委派模型要求除了頂層的啟動類加載器外,其餘的類加載器都應該有自己的父類加載器。父子不會以繼承的關係類實現,而是都是使用組合關係來服用父加載器的代碼。
        在java.lang.ClassLoader的loadClass()方法中實現。
      • 工作過程

        • 一個類加載器首先將類加載請求轉發到父類加載器,只有當父類加載器無法完成(它的搜索範圍中沒有找到所需要的類)時才嘗試自己加載
      • 好處

        • Java類隨着它的類加載器一起具備了一種帶有優先級的層次關係,從而使得基礎類庫得到同意。

    四、內存分配與回收策略

    Minor GC 和 Full GC

    • Minor GC

      • 發生在新生代的垃圾收集動作,因為新生代對象存活時間很短,因此Minor GC會頻繁執行,執行速度快。
      • 時機

        • Eden不足
    • Full GC

      • 發生在老年區的GC,出現Full GC時往往伴隨着Minor GC,比Minor GC慢10倍以上。
      • 時機

          1. 調用System.gc()
          • 只是建議虛擬機執行Full GC,但是虛擬機不一定真正去執行。
            不建議使用這種方式,而是讓虛擬機管理內存。
          1. 老年代空間不足
          • 常見場景就是大對象和長期存活對象進入老年代。
            盡量避免創建過大的對象以及數組,調大新生代大小,讓對象盡量咋新生代中被回收,不進入老年代。
          1. JDK1.7 之前方法區空間不足
          • 當系統中要加載的類、反射的類和常量較多時,永久代可能會被佔滿,在未配置CMS GC的情況下也會執行Full GC,如果空間仍然不夠則會拋出OOM異常。
            可採用增大方法區空間或轉為使用CMS GC。
          1. 空間分配擔保失敗
          • 發生Minor GC時分配擔保的兩個判斷失敗
          1. Concurrent Mode Failure
          • CMS GC 併發清理階段用戶線程還在執行,不斷有新的浮動垃圾產生,當預留空間不足時報Concurrent Mode Failure錯誤並觸發Full GC。

    內存分配策略

      1. 對象優先在Eden分配
      • 大多數情況下,對象在新生代Eden上分配,當Eden空間不夠時,發起Minor GC,當另外一個Survivor空間不足時則將存活對象通過分配擔保機制提前轉移到老年代。
      1. 大對象直接進入老年代
      • 配置參數-XX:PretenureSizeThreshold,大於此值得對象直接在老年代分配,避免在Eden和Survivor之間的大量內存複製。
      1. 長期存活對象進入老年代
      • 虛擬機為每個對象定義了一個Age計數器,對象在Eden出生並經過Minor GC存活轉移到另一個Survivor空間中時Age++,增加到默認16則轉移到老年代。
      1. 動態對象年齡綁定
      • 虛擬機並不是永遠要求對象的年齡必須到達MaxTenuringThreshold才能晉陞老年代,如果在Survivor中相同年齡所有對象大小總和大於Survivor空間的一半,則年齡大於或等於該年齡的對象直接進入老年代。
      1. 空間分配擔保
      • 在發生Minor GC之前,虛擬機先檢查老年代最大可用的連續空間是否大於新生代的所有對象,如果條件成立,那麼Minor GC可以認為是安全的。
        可以通過HandlePromotionFailure參數設置允許冒險,此時虛擬機將與歷代晉陞到老年區對象的平均大小比較,仍小於則要進行一次Full GC。
        在JDK1.6.24之後HandlePromotionFailure已無作用,即虛擬機默認為true。

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

    【其他文章推薦】

    ※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

    ※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

    ※Google地圖已可更新顯示潭子電動車充電站設置地點!!

    ※帶您來看台北網站建置台北網頁設計,各種案例分享

  • 深入理解java繼承從“我爸是李剛”講起

    深入理解java繼承從“我爸是李剛”講起

    目錄

    前言
    本文主要多方面講解java繼承,旨在讓初學者通俗易懂,至於“我爸是李剛”,反正樓主也不知道誰爸是李剛。
    @

    1、繼承的概述

    1.1、繼承的由來

    至於由來簡單一句話:多個類中存在相同屬性和行為時,將這些內容抽取到單獨一個類中,那麼多個類無需再定義這些屬性和行為。

    繼承描述的是事物之間的所屬關係,這種關係是 is-a 的關係。

    1.2、繼承的定義

    繼承:就是子類繼承父類的屬性行為,使得子類對象具有與父類相同的屬性、相同的行為。子類可以直接訪問父類中的非私有的屬性和行為。

    這裏再聲明一點,父類又稱為超類或者基類。而子類又稱為派生類這點很基礎!

    1.3、繼承的優點

    1. 提高代碼的復用性
    2. 類與類之間產生關係,為多態做了完美的鋪墊(不理解沒關係,之後我會再寫一篇多態的文章)

    雖然繼承的優點很多但是Java只支持單繼承,不支持多繼承

    1.4、繼承的格式

    通過 extends 關鍵字,可以聲明一個子類繼承另外一個父類,定義格式如下:

      class 父類 {
       ... 
       }
       class 子類 extends 父類 { 
       ... 
       } 

    2、關於繼承之後的成員變量

    當類之間產生了關係后,其中各類中的成員變量,產生了哪些影響呢? 關於繼承之後的成員變量要從兩方面下手,一是成員變量不重名方面,二是成員變量重名方面。

    2.1、成員變量不重名

    如果子類父類中出現不重名的成員變量,這時的訪問是沒有影響的。代碼如下:

      class liGang {
            // 父類中的成員變量。
           String name ="李剛";//------------------------------父類成員變量是name
        }
        class LiXiaoGang extends liGang {
            // 子類中的成員變量
            String name2 ="李小剛";//--------------------------子類成員變量是name2
            // 子類中的成員方法
            public void show() {
                // 訪問父類中的name,
                System.out.println("我爸是"+name);
                // 繼承而來,所以直接訪問。
                // 訪問子類中的name2
                System.out.println("我是"+name2);
            }
        }
    public class Demo {
            public static void main(String[] args) {
                // 創建子類對象
                LiXiaoGang z = new LiXiaoGang();
                // 調用子類中的show方法
                z.show();
            }
        }
        //演示結果: 我爸是李剛   我是李小剛

    2.2、 成員變量重名

    如果子類父類中出現重名的成員變量,這時的訪問是有影響的。代碼如下:

    class liGang {
            // 父類中的成員變量。
           String name ="李剛";//------------------------------父類成員變量是name
        }
        class LiXiaoGang extends liGang {
            // 子類中的成員變量
            String name ="李小剛";//---------------------------子類成員變量也是name
            // 子類中的成員方法
            public void show() {
                // 訪問父類中的name,
                System.out.println("我爸是"+name);
                // 繼承而來,所以直接訪問。
                // 訪問子類中的name2
                System.out.println("我是"+name);
            }
        }
    public class Demo {
            public static void main(String[] args) {
                // 創建子類對象
                LiXiaoGang z = new LiXiaoGang();
                // 調用子類中的show方法
                z.show();
            }
        }
        //演示結果: 我爸是李小剛   我是李小剛
    
    

    子父類中出現了同名的成員變量時,在子類中需要訪問父類中非私有成員變量時,需要使用 super 關鍵字,至於修飾父類成員變量,類似於之前學過的 this 。 使用格式 super.父類成員變量名

    this表示當前對象,super則表示父類對象,用法類似!

    class liGang {
            // 父類中的成員變量。
           String name ="李剛";
        }
        class LiXiaoGang extends liGang {
            // 子類中的成員變量
            String name ="李小剛";
            // 子類中的成員方法
            public void show() {
                // 訪問父類中的name,
                System.out.println("我爸是"+super.name);
                // 繼承而來,所以直接訪問。
                // 訪問子類中的name2
                System.out.println("我是"+this.name);  //當然this可省略
            }
        }
    public class Demo {
            public static void main(String[] args) {
                // 創建子類對象
                LiXiaoGang z = new LiXiaoGang();
                // 調用子類中的show方法
                z.show();
            }
        }
        //演示結果: 我爸是李剛   我是李小剛

    2.3、關於繼承中成員變量值得思考的一個問題

    同學你有沒有想過這樣一個問題。如果父類中的成員變量
    非私有:子類中可以直接訪問。
    私有:子類是不能直接訪問的。如下:

    當然,同學你要自己體驗體驗編譯報錯過程,看圖沒體驗感不得勁,~嘔,你這無處安放的魅力,無理的要求,我佛了,行吧~

      class liGang2 {
            // 父類中的成員變量。
            private String name ="李剛";
    
        }
        class LiXiaoGang2 extends liGang2 {
            // 子類中的成員變量
            String name ="李小剛";
            // 子類中的成員方法
            public void show() {
                // 訪問父類中的name,
                System.out.println("我爸是"+super.name);//------編譯失敗不能直接訪問父類私有屬性(成員變量)
                // 繼承而來,所以直接訪問。
                // 訪問子類中的name2
                System.out.println("我是"+this.name);  //當然this可省略
            }
        }
    public class PrivateVariable {
            public static void main(String[] args) {
                // 創建子類對象
                ExtendDemo.LiXiaoGang z = new ExtendDemo.LiXiaoGang();
                // 調用子類中的show方法
                z.show();
            }
        }

    通常開發中編碼時,我們遵循封裝的原則,使用private修飾成員變量,那麼如何訪問父類的私有成員變量呢?其實這個時候在父類中提供公共的getXxx方法和setXxx方法就可以了。代碼如下:

    class liGang {
            // 父類中的成員變量。
          private String name ="李剛";
    
            public String getName() {
                return name;
            }
    
            public void setName(String name) {
                this.name = name;
            }
        }
        class LiXiaoGang extends liGang {
            // 子類中的成員變量
            String name ="李小剛";
            // 子類中的成員方法
            public void show() {
                // 訪問父類中的name,
                System.out.println("我爸是"+super.getName());
                // 繼承而來,所以直接訪問。
                // 訪問子類中的name2
                System.out.println("我是"+this.name);  //當然this可省略
            }
        }
    public class Demo {
            public static void main(String[] args) {
                // 創建子類對象
                LiXiaoGang z = new LiXiaoGang();
                // 調用子類中的show方法
                z.show();
            }
        }
        //演示結果: 我爸是李剛   我是李小剛

    分析如下:

    3、關於繼承之後的成員方法

    分析完了成員變量,現在我們一起來分析分析成員方法。
    想一想,當類之間產生了關係,其中各類中的成員方法,又產生了哪些影響呢? 同樣我們依舊從兩方面分析。
    #### 3.1、成員方法不重名
    如果子類父類中出現不重名的成員方法,這時的調用是沒有影響的。對象調用方法時,會先在子類中查找有沒有對 應的方法,若子類中存在就會執行子類中的方法,若子類中不存在就會執行父類中相應的方法。代碼如下:

     class liGang3 {
            // 父類中的成員方法。
           public void zhuangRen1(){//--------------------------父類方法名zhuangRen1
               System.out.println("我叫李剛,人不是我撞的,別抓我,我不認識李小剛");
           }
        }
        class LiXiaoGang3 extends liGang3 {
    
            // 子類中的成員方法
            public void zhuangRen() {//--------------------------子類方法名zhuangRen
                System.out.println("有本事你們告去,我爸是李剛");  
            }
        }
        public class MemberMethod {
            public static void main(String[] args) {
                // 創建子類對象
                LiXiaoGang3 liXiaoGang = new LiXiaoGang3();
                // 調用子類中的show方法
                liXiaoGang.zhuangRen();
                liXiaoGang.zhuangRen1();
            }
        }
        
    打印結果:有本事你們告去,我爸是李剛
            我叫李剛,人不是我撞的,別抓我,我不認識李小剛
    

    #### 3.2、成員方法重名 【方法重寫】
    成員方法重名大體也可以分兩種情況:

    1、方法名相同返回值類型、參數列表卻不相同(優先在子類查找,沒找到就去父類)
    2、方法名、返回值類型、參數列表都相同,沒錯這就是重寫(Override)

    這裏主要講方法重寫 :子類中出現與父類一模一樣的方法時(返回值類型,方法名和參數列表都相同),會出現覆蓋效果,也稱為重寫或者複寫。聲明不變,重新實現。 代碼如下:

        class liGang3 {
            // 父類中的成員方法。
           public void zhuangRen(int a){
               System.out.println("我叫李剛,人不是我撞的,別抓我");
           }
        }
        class LiXiaoGang3 extends liGang3 {
    
            // 子類中的成員方法
            public void zhuangRen(int a) {
                System.out.println("有本事你們告去,我爸是李剛");
            }
        }
        public class MemberMethod {
            public static void main(String[] args) {
                // 創建子類對象
                LiXiaoGang3 liXiaoGang = new LiXiaoGang3();
                // 調用子類中的zhuangRen方法
                liXiaoGang.zhuangRen(1);
    
            }
        }
        結果打印:有本事你們告去,我爸是李剛

    #### 3.3、繼承中重寫方法的意義
    子類可以根據需要,定義特定於自己的行為。既沿襲了父類的功能名稱,又根據子類的需要重新實現父類方法,從而進行擴展增強。比如李剛會開車,李小剛就牛了,在父類中進行擴展增強還會開車撞人,代碼如下:

     class liGang3 {
            // 父類中的成員方法。
           public void kaiChe(){
               System.out.println("我會開車");
           }
        }
        class LiXiaoGang3 extends liGang3 {
            // 子類中的成員方法
            public void kaiChe(){
                super.kaiChe();
                System.out.println("我還會撞人");
                System.out.println("我還能一撞撞倆婆娘");
            }
        }
        public class MemberMethod {
            public static void main(String[] args) {
                // 創建子類對象
                LiXiaoGang3 liXiaoGang = new LiXiaoGang3();
                // 調用子類中的zhuangRen方法
                liXiaoGang.kaiChe();
    
    打印結果:   我會開車
               我還會撞人
               我還能一撞撞倆婆娘
            }
        }
    

    不知道同學們發現了沒有,以上代碼中在子類中使用了 super.kaiChe();super.父類成員方法,表示調用父類的成員方法。

    最後重寫必須注意這幾點:

    1、方法重寫時, 方法名與形參列表必須一致。
    2、子類方法覆蓋父類方法時,必須要保證子類權限 >= 父類權限。
    3、方法重寫時,子類的返回值類型必須要 <= 父類的返回值類型。
    4、方法重寫時,子類拋出的異常類型要 <= 父類拋出的異常類型。

    粗心的同學看黑板,look 這裏【注意:只有訪問權限是>=,返回值、異常類型都是<=

    下面以修飾權限為例,如下:

    4、關於繼承之後的構造方法

    為了讓你更好的體會,首先我先編寫一個程序

       class liGang4 {
            // 父類的無參構造方法。
            public liGang4(){
                System.out.println("父類構造方法執行了。。。");
            }
        }
        class LiXiaoGang4 extends liGang4 {
            // 子類的無參構造方法。
           public LiXiaoGang4(){
               System.out.println("子類構造方法執行了====");
           }
        }
        public class ConstructionDemo {
            public static void main(String[] args) {
                // 創建子類對象
                LiXiaoGang4 z = new LiXiaoGang4();
    
            }
        }

    用一分鐘猜想一下結果是什麼,猜好了再看下面結果:

    父類構造方法執行了。。。
    子類構造方法執行了====

    好了,看了結果之後,你可能有疑惑。父類構造器方法怎麼執行了?我們先來分析分析,首先在main方法中實例化了子類對象,接着會去執行子類的默認構造器初始化,這個時候在構造方法中默認會在第一句代碼中添加super();沒錯,他就是開掛般的存在,不寫也存在的!有的調~讀四聲“跳”~皮的同學就會說,你說存在就存在啊,無憑無據 ~呀,你這個該死的靚仔~ 如下:

    構造方法的名字是與類名一致的,所以子類是無法繼承父類構造方法的。 構造方法的作用是初始化成員變量的。所以子類的初始化過程中,必須先執行父類的初始化動作。子類的構造方法中默認會在第一句代碼中添加super(),表示調用父類的構造方法,父類成員變量初始化后,才可以給子類使用。

    當然我已經強調很多遍了 super() 不寫也默認存在,而且只能是在第一句代碼中,不在第一句代碼中行不行,答案是當然不行,這樣會編譯失敗,如下:

    5、關於繼承的多態性支持的例子

    直接上代碼了喔

    class A{
        public String show(C obj) {
            return ("A and C");
        }
    
        public String show(A obj) {
            return ("A and A");
        }
    
    }
    class B extends A{
        public String show(B obj) {
            return ("B and B");
        }
    }
    class C extends B{
        public String show(A obj) {
            return ("A and B");
        }
    }
    public class Demo1 {
        public static void main(String[] args) {
            A a=new A();
            B b=new B();
            C c=new C();
            System.out.println("第一題 " + a.show(a));
            System.out.println("第二題 " + a.show(b));
            System.out.println("第三題 " + a.show(c));
        }
    }
    運行結果:
            第一題 A and A
            第二題 A and A
            第三題 A and C

    其實吧,第一題和第三題都好理解,第二題就有點意思了,會發現A類中沒有B類型這個參數,這個時候,你就應該知道子類繼承就是父類,換句話說就是子類天然就是父類,比如中國人肯定是人,但是人不一定是中國人(可能是火星人也可能是非洲人),所以父類做為參數類型,直接傳子類的參數進去是可以的,反過來,子類做為參數類型,傳父類的參數進去,就需要強制類型轉換。

    6、super與this的用法

    了解他們的用法之前必須明確一點的是父類空間優先於子類對象產生

    在每次創建子類對象時,先初始化父類空間,再創建其子類對象本身。目的在於子類對象中包含了其對應的父類空間,便可以包含其父類的成員,如果父類成員非private修飾,則子類可以隨意使用父類成員。代碼體現在子類的構 造方法調用時,一定先調用父類的構造方法。理解圖解如下:

    #### 5.1、 super和this的含義:

    super :代表父類的存儲空間標識(可以理解為父親的引用)。

     

    this :代表當前對象的引用(誰調用就代表誰)。

    #### 5.2、 super和this訪問成員

    this.成員變量 ‐‐ 本類的
    super.成員變量 ‐‐ 父類的
    this.成員方法名() ‐‐ 本類的
    super.成員方法名() ‐‐ 父類的

    #### 5.3、super和this訪問構造方法

    this(...) ‐‐ 本類的構造方法
    super(...) ‐‐ 父類的構造方法

    #### 5.4、super()和this()能不能同時使用?

    不能同時使用,thissuper不能同時出現在一個構造函數裏面,因為this必然會調用其它的構造函數,其它的構造函數必然也會有super語句的存在,所以在同一個構造函數裏面有相同的語句,就失去了語句的意義,編譯器也不會通過。
    #### 5.5、總結一下super與this

    子類的每個構造方法中均有默認的super(),調用父類的空參構造。手動調用父類構造會覆蓋默認的super()super()this() 都必須是在構造方法的第一行,所以不能同時出現

    到這裏,java繼承你get到了咩,get到了請咩一聲,隨便隨手~點個讚唄~

    推薦閱讀本專欄的下一篇java文章

    歡迎各位關注我的公眾號,一起探討技術,嚮往技術,追求技術…

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

    【其他文章推薦】

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

  • 徹底搞懂 netty 線程模型

    徹底搞懂 netty 線程模型

    編者注:Netty是Java領域有名的開源網絡庫,特點是高性能和高擴展性,因此很多流行的框架都是基於它來構建的,比如我們熟知的Dubbo、Rocketmq、Hadoop等。本文就netty線程模型展開分析討論下 : )

    IO模型

    • BIO:同步阻塞IO模型;
    • NIO:基於IO多路復用技術的“非阻塞同步”IO模型。簡單來說,內核將可讀可寫事件通知應用,由應用主動發起讀寫操作;
    • AIO:非阻塞異步IO模型。簡單來說,內核將讀完成事件通知應用,讀操作由內核完成,應用只需操作數據即可;應用做異步寫操作時立即返回,內核會進行寫操作排隊並執行寫操作。

    NIO和AIO不同之處在於應用是否進行真正的讀寫操作。

    reactor和proactor模型

    • reactor:基於NIO技術,可讀可寫時通知應用;
    • proactor:基於AIO技術,讀完成時通知應用,寫操作應用通知內核。

    netty線程模型

    netty的線程模型是基於Reactor模型的。

    netty單線程模型

    Reactor 單線程模型,是指所有的 I/O 操作都在同一個 NIO 線程上面完成的,此時NIO線程職責包括:接收新建連接請求、讀寫操作等。

    在一些小容量應用場景下,可以使用單線程模型(注意,Redis的請求處理也是單線程模型,為什麼Redis的性能會如此之高呢?因為Redis的讀寫操作基本都是內存操作,並且Redis協議比較簡潔,序列化/反序列化耗費性能更低)。但是對於高負載、大併發的應用場景卻不合適,主要原因如下:

    • 一個NIO線程同時處理成百上千的連接,性能上無法支撐,即便NIO線程的CPU負荷達到100%,也無法滿足海量消息的編碼、解碼、讀取和發送。
    • 當NIO線程負載過重之後,處理速度將變慢,這會導致大量客戶端連接超時,超時之後往往會進行重發,這更加重了NIO線程的負載,最終會導致大量消息積壓和處理超時,成為系統的性能瓶頸。
    • 可靠性問題:一旦NIO線程意外跑飛,或者進入死循環,會導致整個系統通信模塊不可用,不能接收和處理外部消息,造成節點故障。

    Reactor多線程模型

    Rector 多線程模型與單線程模型最大的區別就是有一組 NIO 線程來處理連接讀寫操作,一個NIO線程處理Accept。一個NIO線程可以處理多個連接事件,一個連接的事件只能屬於一個NIO線程。

    在絕大多數場景下,Reactor 多線程模型可以滿足性能需求。但是,在個別特殊場景中,一個 NIO 線程負責監聽和處理所有的客戶端連接可能會存在性能問題。例如併發百萬客戶端連接,或者服務端需要對客戶端握手進行安全認證,但是認證本身非常損耗性能。在這類場景下,單獨一個 Acceptor 線程可能會存在性能不足的問題,為了解決性能問題,產生了第三種 Reactor 線程模型——主從Reactor 多線程模型。

    Reactor主從多線程模型

    主從 Reactor 線程模型的特點是:服務端用於接收客戶端連接的不再是一個單獨的 NIO 線程,而是一個獨立的 NIO 線程池。Acceptor 接收到客戶端 TCP連接請求並處理完成后(可能包含接入認證等),將新創建的 SocketChannel注 冊 到 I/O 線 程 池(sub reactor 線 程 池)的某個I/O線程上, 由它負責SocketChannel 的讀寫和編解碼工作。Acceptor 線程池僅僅用於客戶端的登錄、握手和安全認證,一旦鏈路建立成功,就將鏈路註冊到後端 subReactor 線程池的 I/O 線程上,由 I/O 線程負責後續的 I/O 操作。

    netty線程模型思考

    netty 的線程模型並不是一成不變的,它實際取決於用戶的啟動參數配置。通過設置不同的啟動參數,Netty 可以同時支持 Reactor 單線程模型、多線程模型。

    為了盡可能地提升性能,Netty 在很多地方進行了無鎖化的設計,例如在 I/O 線程內部進行串行操作,避免多線程競爭導致的性能下降問題。表面上看,串行化設計似乎 CPU 利用率不高,併發程度不夠。但是,通過調整 NIO 線程池的線程參數,可以同時啟動多個串行化的線程并行運行,這種局部無鎖化的串行線程設計相比一個隊列多個工作線程的模型性能更優。(小夥伴們後續多線程併發流程可參考該類實現方案

    Netty 的 NioEventLoop 讀取到消息之後,直接調用 ChannelPipeline 的fireChannelRead (Object msg)。 只要用戶不主動切換線程, 一直都是由NioEventLoop 調用用戶的 ChannelHandler,期間不進行線程切換。這種串行化處理方式避免了多線程操作導致的鎖的競爭,從性能角度看是最優的。

    Netty擁有兩個NIO線程池,分別是bossGroupworkerGroup,前者處理新建連接請求,然後將新建立的連接輪詢交給workerGroup中的其中一個NioEventLoop來處理,後續該連接上的讀寫操作都是由同一個NioEventLoop來處理。注意,雖然bossGroup也能指定多個NioEventLoop(一個NioEventLoop對應一個線程),但是默認情況下只會有一個線程,因為一般情況下應用程序只會使用一個對外監聽端口。

    這裏試想一下,難道不能使用多線程來監聽同一個對外端口么,即多線程epoll_wait到同一個epoll實例上?

    epoll相關的主要兩個方法是epoll_wait和epoll_ctl,多線程同時操作同一個epoll實例,那麼首先需要確認epoll相關方法是否線程安全:簡單來說,epoll是通過鎖來保證線程安全的, epoll中粒度最小的自旋鎖ep->lock(spinlock)用來保護就緒的隊列, 互斥鎖ep->mtx用來保護epoll的重要數據結構紅黑樹

    看到這裏,可能有的小夥伴想到了Nginx多進程針對監聽端口的處理策略,Nginx是通過accept_mutex機制來保證的。accept_mutex是nginx的(新建連接)負載均衡鎖,讓多個worker進程輪流處理與client的新連接。當某個worker進程的連接數達到worker_connections配置(單個worker進程的最大處理連接數)的最大連接數的7/8時,會大大減小獲取該worker獲取accept鎖的概率,以此實現各worker進程間的連接數的負載均衡。accept鎖默認打開,關閉它時nginx處理新建連接耗時會更短,但是worker進程之間可能連接不均衡,並且存在“驚群”問題。只有在使能accept_mutex並且當前系統不支持原子鎖時,才會用文件實現accept鎖。注意,accept_mutex加鎖失敗時不會阻塞當前線程,類似tryLock。

    現代linux中,多個socker同時監聽同一個端口也是可行的,nginx 1.9.1也支持這一行為。linux 3.9以上內核支持SO_REUSEPORT選項,允許多個socker bind/listen在同一端口上。這樣,多個進程可以各自申請socker監聽同一端口,當連接事件來臨時,內核做負載均衡,喚醒監聽的其中一個進程來處理,reuseport機制有效的解決了epoll驚群問題。

    再回到剛才提出的問題,java中多線程來監聽同一個對外端口,epoll方法是線程安全的,這樣就可以使用使用多線程監聽epoll_wait了么,當然是不建議這樣乾的,除了epoll的驚群問題之外,還有一個就是,一般開發中我們使用epoll設置的是LT模式(水平觸發方式,與之相對的是ET默認,前者只要連接事件未被處理就會在epoll_wait時始終觸發,後者只會在真正有事件來時在epoll_wait觸發一次),這樣的話,多線程epoll_wait時就會導致第一個線程epoll_wait之後還未處理完畢已發生的事件時,第二個線程也會epoll_wait返回,顯然這不是我們想要的,關於java nio的測試demo如下:

    public class NioDemo {
        private static AtomicBoolean flag = new AtomicBoolean(true);
        public static void main(String[] args) throws Exception {
            ServerSocketChannel serverChannel = ServerSocketChannel.open();
            serverChannel.socket().bind(new InetSocketAddress(8080));
            // non-block io
            serverChannel.configureBlocking(false);
            Selector selector = Selector.open();
            serverChannel.register(selector, SelectionKey.OP_ACCEPT);
    
            // 多線程執行
            Runnable task = () -> {
                try {
                    while (true) {
                        if (selector.select(0) == 0) {
                            System.out.println("selector.select loop... " + Thread.currentThread().getName());
                            Thread.sleep(1);
                            continue;
                        }
    
                        if (flag.compareAndSet(true, false)) {
                            System.out.println(Thread.currentThread().getName() + " over");
                            return;
                        }
    
                        Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
                        while (iter.hasNext()) {
                            SelectionKey key = iter.next();
    
                            // accept event
                            if (key.isAcceptable()) {
                                handlerAccept(selector, key);
                            }
    
                            // socket event
                            if (key.isReadable()) {
                                handlerRead(key);
                            }
    
                            /**
                             * Selector不會自己從已選擇鍵集中移除SelectionKey實例,必須在處理完通道時手動移除。
                             * 下次該通道變成就緒時,Selector會再次將其放入已選擇鍵集中。
                             */
                            iter.remove();
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            };
    
            List<Thread> threadList = new ArrayList<>();
            for (int i = 0; i < 2; i++) {
                Thread thread = new Thread(task);
                threadList.add(thread);
                thread.start();
            }
            for (Thread thread : threadList) {
                thread.join();
            }
            System.out.println("main end");
        }
    
        static void handlerAccept(Selector selector, SelectionKey key) throws Exception {
            System.out.println("coming a new client... " + Thread.currentThread().getName());
            Thread.sleep(10000);
            SocketChannel channel = ((ServerSocketChannel) key.channel()).accept();
            channel.configureBlocking(false);
            channel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
        }
    
        static void handlerRead(SelectionKey key) throws Exception {
            SocketChannel channel = (SocketChannel) key.channel();
            ByteBuffer buffer = (ByteBuffer) key.attachment();
            buffer.clear();
    
            int num = channel.read(buffer);
            if (num <= 0) {
                // error or fin
                System.out.println("close " + channel.getRemoteAddress());
                channel.close();
            } else {
                buffer.flip();
                String recv = Charset.forName("UTF-8").newDecoder().decode(buffer).toString();
                System.out.println("recv: " + recv);
    
                buffer = ByteBuffer.wrap(("server: " + recv).getBytes());
                channel.write(buffer);
            }
        }
    }

    netty線程模型實踐

    (1) 時間可控的簡單業務直接在 I/O 線程上處理

    時間可控的簡單業務直接在 I/O 線程上處理,如果業務非常簡單,執行時間非常短,不需要與外部網絡交互、訪問數據庫和磁盤,不需要等待其它資源,則建議直接在業務 ChannelHandler 中執行,不需要再啟業務的線程或者線程池。避免線程上下文切換,也不存在線程併發問題。

    (2) 複雜和時間不可控業務建議投遞到後端業務線程池統一處理

    複雜度較高或者時間不可控業務建議投遞到後端業務線程池統一處理,對於此類業務,不建議直接在業務 ChannelHandler 中啟動線程或者線程池處理,建議將不同的業務統一封裝成 Task,統一投遞到後端的業務線程池中進行處理。過多的業務ChannelHandler 會帶來開發效率和可維護性問題,不要把 Netty 當作業務容器,對於大多數複雜的業務產品,仍然需要集成或者開發自己的業務容器,做好和Netty 的架構分層。

    (3) 業務線程避免直接操作 ChannelHandler

    業務線程避免直接操作 ChannelHandler,對於 ChannelHandler,IO 線程和業務線程都可能會操作,因為業務通常是多線程模型,這樣就會存在多線程操作ChannelHandler。為了盡量避免多線程併發問題,建議按照 Netty 自身的做法,通過將操作封裝成獨立的 Task 由 NioEventLoop 統一執行,而不是業務線程直接操作,相關代碼如下所示:

    如果你確認併發訪問的數據或者併發操作是安全的,則無需多此一舉,這個需要根據具體的業務場景進行判斷,靈活處理。

    推薦閱讀

    歡迎小夥伴關注【TopCoder】閱讀更多精彩好文。

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

    【其他文章推薦】

    ※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

    ※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

    ※Google地圖已可更新顯示潭子電動車充電站設置地點!!

    ※帶您來看台北網站建置台北網頁設計,各種案例分享

  • 北京公車採購案 八成為電動車

    北京公交集團持續落實首都空氣清淨規劃,2016年計畫採購2700輛新的公車,其中有81%將採購電動車款,以減少大眾運輸系統的碳排放量。

    北京霧霾問題嚴重,改用電動車來取代汽油車是減輕空氣汙染的方法之一。2015年間,北京公交集團共置換了2306輛汽油公車為新式環保公車,其中有一半以上是新能源電動車。同時,2015年改造超過8000輛柴油公車,減少60%的氮氧化合物排放量。

    在純電動車基礎設施方面,北京公交集團陸續興建吳癸電動車線路網、變電站、充電站網絡等,共有21個公車站可供純電動車充電。

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

    【其他文章推薦】

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

  • 北汽豪擲80億元佈局新能源汽車

    2015年12月28日,北京汽車集團有限公司就“北汽新能源汽車動力電池”、“北汽集團常州產業基地”兩個專案,與常州市政府簽約。

    此次北汽集團擬在常州建設的兩個項目總投資80億元,其中,北汽新能源汽車動力電池專案總投資約30億元,規劃動力電池產能達到5G瓦時,同時將以滆湖低碳濕地公園培訓中心為主體,打造北汽新能源綠色商學院,其主要目的是加強新體系電池基礎研究和關鍵技術開發,推進新一代鋰離子電池的工程化和產業化,實現對動力電池產業鏈核心環節資源掌控,以支撐北汽新能源業務需求。

    而北汽集團常州產業基地專案總投資50億元,總規劃年產30萬輛整車及配套零部件、物流專案,其中一期年產15萬輛SUV、MPV和輕型客車,二期重點生產新能源汽車,打造產業生態鏈。

    此前,北汽集團總投資100億元的新能源汽車和總投資50億元的通用航空兩個項目已經於今年4月和10月相繼落戶常州。

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

    【其他文章推薦】

    ※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

    ※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

    ※Google地圖已可更新顯示潭子電動車充電站設置地點!!

    ※帶您來看台北網站建置台北網頁設計,各種案例分享

  • 010.Kubernetes二進制部署kube-controller-manager

    010.Kubernetes二進制部署kube-controller-manager

    一 部署高可用kube-controller-manager

    1.1 高可用kube-controller-manager介紹


    本實驗部署一個三實例 kube-controller-manager 的集群,啟動后將通過競爭選舉機制產生一個 leader 節點,其它節點為阻塞狀態。當 leader 節點不可用時,阻塞的節點將再次進行選舉產生新的 leader 節點,從而保證服務的可用性。

    為保證通信安全,本文檔先生成 x509 證書和私鑰,kube-controller-manager 在如下兩種情況下使用該證書:

    • 與 kube-apiserver 的安全端口通信;
    • 在安全端口(https,10252) 輸出 prometheus 格式的 metrics。

    1.2 創建kube-controller-manager證書和私鑰

      1 [root@k8smaster01 ~]# cd /opt/k8s/work
      2 [root@k8smaster01 work]# cat > kube-controller-manager-csr.json <<EOF
      3 {
      4   "CN": "system:kube-controller-manager",
      5   "hosts": [
      6     "127.0.0.1",
      7     "172.24.8.71",
      8     "172.24.8.72",
      9     "172.24.8.73"
     10   ],
     11   "key": {
     12     "algo": "rsa",
     13     "size": 2048
     14   },
     15   "names": [
     16     {
     17       "C": "CN",
     18       "ST": "Shanghai",
     19       "L": "Shanghai",
     20       "O": "system:kube-controller-manager",
     21       "OU": "System"
     22     }
     23   ]
     24 }
     25 EOF
     26 #創建kube-controller-manager的CA證書請求文件



    解釋:

    hosts 列表包含所有 kube-controller-manager 節點 IP;

    CN 和 O 均為 system:kube-controller-manager,kubernetes 內置的 ClusterRoleBindings system:kube-controller-manager 賦予 kube-controller-manager 工作所需的權限。



      1 [root@k8smaster01 ~]# cd /opt/k8s/work
      2 [root@k8smaster01 work]# cfssl gencert -ca=/opt/k8s/work/ca.pem \
      3 -ca-key=/opt/k8s/work/ca-key.pem -config=/opt/k8s/work/ca-config.json \
      4 -profile=kubernetes kube-controller-manager-csr.json | cfssljson -bare kube-controller-manager	#生成CA密鑰(ca-key.pem)和證書(ca.pem)


    1.3 分發證書和私鑰

      1 [root@k8smaster01 ~]# cd /opt/k8s/work
      2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
      3 [root@k8smaster01 work]# for master_ip in ${MASTER_IPS[@]}
      4   do
      5     echo ">>> ${master_ip}"
      6     scp kube-controller-manager*.pem root@${master_ip}:/etc/kubernetes/cert/
      7   done


    1.4 創建和分發kubeconfig


    kube-controller-manager 使用 kubeconfig 文件訪問 apiserver,該文件提供了 apiserver 地址、嵌入的 CA 證書和 kube-controller-manager 證書:

      1 [root@k8smaster01 ~]# cd /opt/k8s/work
      2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
      3 [root@k8smaster01 work]# kubectl config set-cluster kubernetes \
      4   --certificate-authority=/opt/k8s/work/ca.pem \
      5   --embed-certs=true \
      6   --server=${KUBE_APISERVER} \
      7   --kubeconfig=kube-controller-manager.kubeconfig
      8 
      9 [root@k8smaster01 work]# kubectl config set-credentials system:kube-controller-manager \
     10   --client-certificate=kube-controller-manager.pem \
     11   --client-key=kube-controller-manager-key.pem \
     12   --embed-certs=true \
     13   --kubeconfig=kube-controller-manager.kubeconfig
     14 
     15 [root@k8smaster01 work]# kubectl config set-context system:kube-controller-manager \
     16   --cluster=kubernetes \
     17   --user=system:kube-controller-manager \
     18   --kubeconfig=kube-controller-manager.kubeconfig
     19 
     20 [root@k8smaster01 work]# kubectl config use-context system:kube-controller-manager --kubeconfig=kube-controller-manager.kubeconfig
     21 
     22 [root@k8smaster01 ~]# cd /opt/k8s/work
     23 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
     24 [root@k8smaster01 work]# for master_ip in ${MASTER_IPS[@]}
     25   do
     26     echo ">>> ${master_ip}"
     27     scp kube-controller-manager.kubeconfig root@${master_ip}:/etc/kubernetes/
     28   done


    1.5 創建kube-controller-manager的systemd

      1 [root@k8smaster01 ~]# cd /opt/k8s/work
      2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
      3 [root@k8smaster01 work]# cat > kube-controller-manager.service.template <<EOF
      4 [Unit]
      5 Description=Kubernetes Controller Manager
      6 Documentation=https://github.com/GoogleCloudPlatform/kubernetes
      7 
      8 [Service]
      9 WorkingDirectory=${K8S_DIR}/kube-controller-manager
     10 ExecStart=/opt/k8s/bin/kube-controller-manager \\
     11   --profiling \\
     12   --cluster-name=kubernetes \\
     13   --controllers=*,bootstrapsigner,tokencleaner \\
     14   --kube-api-qps=1000 \\
     15   --kube-api-burst=2000 \\
     16   --leader-elect \\
     17   --use-service-account-credentials\\
     18   --concurrent-service-syncs=2 \\
     19   --bind-address=##MASTER_IP## \\
     20   --secure-port=10252 \\
     21   --tls-cert-file=/etc/kubernetes/cert/kube-controller-manager.pem \\
     22   --tls-private-key-file=/etc/kubernetes/cert/kube-controller-manager-key.pem \\
     23   --port=0 \\
     24   --authentication-kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \\
     25   --client-ca-file=/etc/kubernetes/cert/ca.pem \\
     26   --requestheader-allowed-names="" \\
     27   --requestheader-client-ca-file=/etc/kubernetes/cert/ca.pem \\
     28   --requestheader-extra-headers-prefix="X-Remote-Extra-" \\
     29   --requestheader-group-headers=X-Remote-Group \\
     30   --requestheader-username-headers=X-Remote-User \\
     31   --authorization-kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \\
     32   --cluster-signing-cert-file=/etc/kubernetes/cert/ca.pem \\
     33   --cluster-signing-key-file=/etc/kubernetes/cert/ca-key.pem \\
     34   --experimental-cluster-signing-duration=8760h \\
     35   --horizontal-pod-autoscaler-sync-period=10s \\
     36   --concurrent-deployment-syncs=10 \\
     37   --concurrent-gc-syncs=30 \\
     38   --node-cidr-mask-size=24 \\
     39   --service-cluster-ip-range=${SERVICE_CIDR} \\
     40   --pod-eviction-timeout=6m \\
     41   --terminated-pod-gc-threshold=10000 \\
     42   --root-ca-file=/etc/kubernetes/cert/ca.pem \\
     43   --service-account-private-key-file=/etc/kubernetes/cert/ca-key.pem \\
     44   --kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \\
     45   --logtostderr=true \\
     46   --v=2
     47 Restart=on-failure
     48 RestartSec=5
     49 
     50 [Install]
     51 WantedBy=multi-user.target
     52 EOF


    1.6 分發systemd

      1 [root@k8smaster01 ~]# cd /opt/k8s/work
      2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
      3 [root@k8smaster01 work]# for (( i=0; i < 3; i++ ))
      4   do
      5     sed -e "s/##MASTER_NAME##/${MASTER_NAMES[i]}/" -e "s/##MASTER_IP##/${MASTER_IPS[i]}/" kube-controller-manager.service.template > kube-controller-manager-${MASTER_IPS[i]}.service
      6   done						#修正相應IP
      7 [root@k8smaster01 work]# ls kube-controller-manager*.service
      8 [root@k8smaster01 ~]# cd /opt/k8s/work
      9 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
     10 [root@k8smaster01 work]# for master_ip in ${MASTER_IPS[@]}
     11   do
     12     echo ">>> ${master_ip}"
     13     scp kube-controller-manager-${master_ip}.service root@${master_ip}:/etc/systemd/system/kube-controller-manager.service
     14   done						#分發system


    二 啟動並驗證

    2.1 啟動kube-controller-manager 服務

      1 [root@k8smaster01 ~]# cd /opt/k8s/work
      2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
      3 [root@k8smaster01 work]# for master_ip in ${MASTER_IPS[@]}
      4   do
      5     echo ">>> ${master_ip}"
      6     ssh root@${master_ip} "mkdir -p ${K8S_DIR}/kube-controller-manager"
      7     ssh root@${master_ip} "systemctl daemon-reload && systemctl enable kube-controller-manager && systemctl restart kube-controller-manager"
      8   done


    2.2 檢查kube-controller-manager 服務

      1 [root@k8smaster01 ~]# source /opt/k8s/bin/environment.sh
      2 [root@k8smaster01 ~]# for master_ip in ${MASTER_IPS[@]}
      3   do
      4     echo ">>> ${master_ip}"
      5     ssh root@${master_ip} "systemctl status kube-controller-manager|grep Active"
      6   done



    2.3 查看輸出的 metrics

      1 [root@k8smaster01 ~]# curl -s --cacert /opt/k8s/work/ca.pem --cert /opt/k8s/work/admin.pem --key /opt/k8s/work/admin-key.pem https://172.24.8.71:10252/metrics |head


    注意:以上命令在 kube-controller-manager 節點上執行。

    2.4 查看權限

      1 [root@k8smaster01 ~]# kubectl describe clusterrole system:kube-controller-manager



    ClusteRole system:kube-controller-manager 的權限很小,只能創建 secret、serviceaccount 等資源對象,各 controller 的權限分散到 ClusterRole system:controller:XXX 中。

    當在 kube-controller-manager 的啟動參數中添加 –use-service-account-credentials=true 參數,這樣 main controller 會為各 controller 創建對應的 ServiceAccount XXX-controller。內置的 ClusterRoleBinding system:controller:XXX 將賦予各 XXX-controller ServiceAccount 對應的 ClusterRole system:controller:XXX 權限。

      1 [root@k8smaster01 ~]# kubectl get clusterrole|grep controller



    如deployment controller:

      1 [root@k8smaster01 ~]# kubectl describe clusterrole system:controller:deployment-controller


    2.5 查看當前leader

      1 [root@k8smaster01 ~]# kubectl get endpoints kube-controller-manager --namespace=kube-system  -o yaml



    kubelet 認證和授權:https://kubernetes.io/docs/admin/kubelet-authentication-authorization/#kubelet-authorization
    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

  • 代碼注入及其拓展–逆向開發

    代碼注入及其拓展–逆向開發

    今天繼續講述逆向開發中另一個比較重要的課程是代碼注入內容,本篇篇幅比較長,但還是有很多乾貨的,希望大家通過此篇文章更加了解逆向開發中的要點和知識點.我們將分解幾個內容,進行講解:

    1. Framework注入
    2. Dylib注入
    3. MethodSwizzle
    4. 微信示例講解
    5. 總結

    讓代碼執行自己的代碼,整體方案如下:

    如何讓別人的app來執行自己的代碼呢? 這就要通過代碼注入的方式來達到,而代碼注入的方式有兩種: 一種是通過framework, 一種是dylib方式,另種方案,可以通過Runtime機制

    代碼注入思路:

    DYLD會動態加載動態庫Framework中所有動態庫,在frameworks中加入自己的一個動態庫,然後在動態庫中hook和注入代碼.

    一、FrameWork注入

     1.準備工作

    • 微信6.6.5(越獄應用)
    • MachOView軟件

      MachOView的下載地址:

      如果想看源碼如下:MachOView源碼:

    • yololib工具(給MachOView注入framework)

      yololib工具下載地址:

    • 簽名文件appsign文件

    2.流程

    2.1 加入準備工作,導入微信6.6.5版本以及腳本appSign.sh重簽名文件

    2.2 將appSign導入到項目腳本中

     

     

     2.3 有了上面的兩個步驟后,然後編譯一下工程,會出現一個temp工程,裡面包含了payload文件

    2.4 显示包內容,查看可執行文件

     2.5 我們通過MachOView軟件查看WeChat

    我們看到有很多的DYLIB,代表的是加載動態庫

    2.6  我們在項目中新建framework

     

    2.7 新建文件用於驗證

    2.8 想要達到剛加載就運行,代碼要寫在load方法

     2.9 編譯一下,查看app包位置會多出一個framework

    2.10 显示包內容,在framework查看

    由上可知,WJHookFrameWork已經加入成功。

    2.11 但是運行並沒有成功,沒有執行load里的代碼

    原因:用MachOView打開可執行的WeChat,沒有找到WJHookFrameWork

    下面我們講述怎麼將WJHookFramework寫入到MachoView文件中?

    3. WJHookFramework寫入到MachOView文件中

    需要使用yololib工具,建議將yololib放到 /usr/local/bin

    3.1 解壓越獄包

    3.2 將WeChat.app显示包內容,找到WeChat可執行的文件

    需要增加執行權限: chmod +x WeChat

    3.3 寫入WeChat可執行文件

    yololib WeChat Frameworks/WJHookFrameWork.framework/WJHookFrameWork

    通過上面的過程,查看MachOView文件Load commands中是否有WJHookFrameWork

    上面圖显示已經加入成功。

    3.4 刪除原有的ipa,打包payload

    zip -ry WeChat.ipa Payload

    將WeChat.ipa放入App目錄中,刪除其他的文件夾。

     

    3.5 再次運行,發現成功!!!

    上面就是framework方式代碼注入。大家可以私信我,如有不懂!!!

     二、Dylib注入

    2.1 新建工程,添加腳本到build phases 


    加入腳本文件

    2.2添加第三方庫dylib(mac os的,非ios)

    2.3 添加依賴

    2.4 修改第三方類庫僅限mac使用,修改Base SDK

    2.5 修改signing 

    2.6 腳本中注入動態庫的代碼

    # ${SRCROOT} 它是工程文件所在的目錄
    TEMP_PATH="${SRCROOT}/Temp"
    #資源文件夾,我們提前在工程目錄下新建一個APP文件夾,裏面放ipa包
    ASSETS_PATH="${SRCROOT}/APP"
    #目標ipa包路徑
    TARGET_IPA_PATH="${ASSETS_PATH}/*.ipa"
    #清空Temp文件夾
    rm -rf "${SRCROOT}/Temp"
    mkdir -p "${SRCROOT}/Temp"
    
    #----------------------------------------
    # 1. 解壓IPA到Temp下
    unzip -oqq "$TARGET_IPA_PATH" -d "$TEMP_PATH"
    # 拿到解壓的臨時的APP的路徑
    TEMP_APP_PATH=$(set -- "$TEMP_PATH/Payload/"*.app;echo "$1")
    # echo "路徑是:$TEMP_APP_PATH"
    
    #----------------------------------------
    # 2. 將解壓出來的.app拷貝進入工程下
    # BUILT_PRODUCTS_DIR 工程生成的APP包的路徑
    # TARGET_NAME target名稱
    TARGET_APP_PATH="$BUILT_PRODUCTS_DIR/$TARGET_NAME.app"
    echo "app路徑:$TARGET_APP_PATH"
    
    rm -rf "$TARGET_APP_PATH"
    mkdir -p "$TARGET_APP_PATH"
    cp -rf "$TEMP_APP_PATH/" "$TARGET_APP_PATH"
    
    #----------------------------------------
    # 3. 刪除extension和WatchAPP.個人證書沒法簽名Extention
    rm -rf "$TARGET_APP_PATH/PlugIns"
    rm -rf "$TARGET_APP_PATH/Watch"
    
    #----------------------------------------
    # 4. 更新info.plist文件 CFBundleIdentifier
    #  設置:"Set : KEY Value" "目標文件路徑"
    /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $PRODUCT_BUNDLE_IDENTIFIER" "$TARGET_APP_PATH/Info.plist"
    
    #----------------------------------------
    # 5. 給MachO文件上執行權限
    # 拿到MachO文件的路徑
    APP_BINARY=`plutil -convert xml1 -o - $TARGET_APP_PATH/Info.plist|grep -A1 Exec|tail -n1|cut -f2 -d\>|cut -f1 -d\<`
    #上可執行權限
    chmod +x "$TARGET_APP_PATH/$APP_BINARY"
    
    #----------------------------------------
    # 6. 重簽名第三方 FrameWorks
    TARGET_APP_FRAMEWORKS_PATH="$TARGET_APP_PATH/Frameworks"
    if [ -d "$TARGET_APP_FRAMEWORKS_PATH" ];
    then
    for FRAMEWORK in "$TARGET_APP_FRAMEWORKS_PATH/"*
    do
    
    #簽名
    /usr/bin/codesign --force --sign "$EXPANDED_CODE_SIGN_IDENTITY" "$FRAMEWORK"
    done
    fi
    
    #注入
    yololib "$TARGET_APP_PATH/$APP_BINARY" "Frameworks/libHankHook.dylib"

    2.7 編譯運行成功(也和上面一樣在類中加入load代碼)

     

    上面就是dylib方式代碼注入,希望對大家有所幫助!!!

     通過上面的兩種方式實現代碼注入,讓別人的app運行自己的app,下面總結如下:

     三、MethodSwizzle

    3.1 概念

    iOS 中實現AOP編程思想的方式其中之一是Method Swizzling,而 Method Swizzling 是利用Runtime特性把一個方法和另個方法的實現做替換,程序運行時修改Dispatch Table里SEL和IMP之間的映射關係.

    通過swizzling method改變目標函數selector所指向實現,在新的實現中來實現所要改的內容即可.

    3.2 特點

    • 繼承: 修改較多,無法敢保證他人一定繼承基類
    • 類別: 類別中重寫方法會覆蓋到原有的實現,其實,在真實的開發中,重寫方法並不是為了取代它,而是為了添加一些實現; 如果幾個類別實現了同樣的方法, 但只有一個類別的方法會被調用.
    • AOP優勢: 減少了重複代碼

    3.3 代碼

    @implementation NSURL (HKURL)
    
    +(void)load
    {
        Method URLWithStr = class_getClassMethod(self, @selector(URLWithString:));
        
        Method HKURL = class_getClassMethod(self, @selector(HKURLWithStr:));
        
        //交換
        method_exchangeImplementations(URLWithStr, HKURL);
    }
    
    +(instancetype)HKURLWithStr:(NSString *)str{
        //調用系統原來的方法
        NSURL * url = [NSURL HKURLWithStr:str];
        if (url == nil) {
            str = @"https://www.blog.com";
        }
        url = [NSURL HKURLWithStr:str];
        
        return url;
    }

    在上面的代碼中,利用method swizzling的交換方法.其他Runtime的使用方法,以及為什麼寫在load方法中,請參考本人另篇博客

    拓展: 為什麼寫在load中?

    • load方法在源文件被裝載到程序中會被自動調用,不需要手動調用,也不需要該類使用不使用無關,在main()前被執行.
    • 當子類重寫了load,假如子類的類別重寫了load,load的調用順序會這樣: 父類、子類、子類類別
    • 但是如果有多個子類category都重寫了load,每個子類category中load都會調用一次
    • 假如子類沒有重寫load,子類的默認load也不會去調用父類的load.此與正常繼承不太一樣.
    • 在正常的開發中, 除了method swizzle ,其他的邏輯代碼盡量不放在load,load方法中的代碼邏輯要盡量簡單

     

    四、微信示例Demo

    4.1 微信–破壞註冊

    4.1.1 將微信程序運行出來,如下圖所示

    4.1.2 根據上面紅色找出類名,方法名

    4.1.3 通過插件class-dump導出微信的.h文件

    class-dump是將OC運行時聲明的信息導出來的工具, 其實可以導出.h文件. 用此工具將未經過加密的app的頭文件導出來.

    使用它同樣也要講此工具拷貝到MAC的目錄下/usr/local/bin下.

    4.1.4 經過sublime text來找出對應的文件

    4.1.5 通過第三部分講解,利用runtime交換方法

    4.1.6 結果

     

    4.2 竊取微信密碼

    4.2.1 找到輸入密碼框的內容

    從上面看出,登錄按鈕為一個FixTitleColorButton對象,Target名字存放的地址為0x280afaa40,Action名字存放地址是0x280afac00。

    4.2.2 查看賬號密碼的輸入框

    發現賬號密碼輸入框對象屬於都一個對象,叫做WCUITextField

    4.2.3 利用LLDB查看登錄具體的Target和Action

    從上面卡出,登錄按鈕在WCAccountMainLoginViewController頁面中;

    登錄點擊方法叫做onNext

    4.2.4 利用Sublime查看WeChat文件

    發現確實有onNext()方法,並從中看出賬號輸入框和密碼輸入框都是WCAccountTextFieldItem中,但是並沒有發現textFileld,但是可以看到WCAccountTextFieldItem是繼承於WCBaseTextFieldItem,我們再看看WCBaseTextFieldItem文件內容

    看出一個m_textField對象,通過tex字段取出string。

    4.2.5 在賬號欄中輸入賬號和密碼

    po [(WCAccountMainLoginViewController *)0x1128bbc00 valueForKey:@"_textFieldUserPwdItem"]
    po [(WCAccountTextFieldItem *)0x28328e880 valueForKey:@"m_textField"]
    po [(WCUITextField *)0x112163a00 text]

    通過LLDB調試輸入的密碼是123456。

    4.2.6 Hook登錄,獲取密碼

    + (void)load {
        NSLog(@"來了,老弟");
        Method onNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), sel_registerName("onNext"));
        //1.保存原始的IMP
        old_onNext = method_getImplementation(onNext);
        //2.SET
        method_setImplementation(onNext, (IMP)my_next);
    }
    
    IMP (*old_onNext)(id self,SEL _cmd);
    
    void my_next(id self,SEL _cmd){
        // 獲取密碼
        NSString *pwd = [[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"] performSelector:@selector(text)];
        NSString *accountTF = [[[self valueForKey:@"_textFieldUserNameItem"] valueForKey:@"m_textField"] performSelector:@selector(text)];
        NSLog(@"密碼是!%@",pwd);
        // 將密碼追加在賬號欄的後面
        [[[self valueForKey:@"_textFieldUserNameItem"] valueForKey:@"m_textField"] performSelector:@selector(setText:) withObject:[NSString stringWithFormat:@"%@+%@",accountTF,pwd]];
        //調用原來的方法
        old_onNext(self,_cmd);
    }

    上面用的是setIMP和getIMP的方式,對原方法進行Hook,也可以用class_replaceMethod(),method_exchangeImplementations()。

     

    五、總結

    首先從代碼注入的方式:framework和dylib兩種方式,然後講到Method swizzling方式嘗試Hook,最後又以demo的方式來闡述代碼注入和Hook,希望對大家理解逆向開發的代碼注入有所幫助!!!,歡迎大家繼續關注!!!

     

     

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

    【其他文章推薦】

    ※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

    ※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

    ※Google地圖已可更新顯示潭子電動車充電站設置地點!!

    ※帶您來看台北網站建置台北網頁設計,各種案例分享

  • 對於計算機相關專業我們在迷茫什麼

    對於計算機相關專業我們在迷茫什麼

    計算機相關專業初識–對於計算機相關專業我們在迷茫什麼

    前言

    由於種種原因,迫使我寫下這篇博客,我相信,初入計算機相關專業的萌新肯定很迷茫,我該學什麼,我該如何去學,我該如何學好等等問題纏繞心頭。有很多學弟學妹問我該如何去學計算機相關專業,作為過來人,我決定將我的所知所得寫下來,讓初入計算機相關專業的萌新的學習之路走得更順暢一些。

    一、什麼是計算機

    對於剛學習計算機相關專業的萌新來說,了解一下計算機的工作原理是十分必要的,但是在這裏我們不過多闡述,讓大家簡單了解一下就好。

    讓我們先來看一下對於計算機名詞的解釋:

    計算機(computer)俗稱電腦,是現代一種用於高速計算的电子計算機器,可以進行數值計算,又可以進行邏輯計算,還具有存儲記憶功能。是能夠按照程序運行,自動、高速處理海量數據的現代化智能电子設備。

    划重點:

    • 我們注意到,計算機就是一種用於進行數值計算的現代化智能电子設備。需要理解的是為什麼是進行數值計算,在這裏,你會疑惑,為什麼是數值計算呢,我輸入的明明不是数字呀?這個問題很容易解釋清楚,因為計算機只是一種电子設備,它不具有人類獨立思考和不斷學習的能力,它的所有功能都是事先設定好的,所以當計算機面對輸入字符的時候,會將它統一按照ASCII(計算機編碼系統)規則轉換為數值“0”和“1”(二進制數值),所以,在計算機里,數據存儲都是用“0”和“1”(即二進制數值)來實現。

    • 還有一點值得注意,按照程序運行,那麼問題來了,程序是什麼?程序就是一組計算機能識別和執行的指令, 它以某些程序設計語言編寫,運行於某種目標結構體繫上 。舉個例子,程序就像是用英語(程序設計語言,例如c,c++)寫的文章,要讓一個懂的英語的人(編譯器,如C的編譯器gcc,這裏要注意編譯器和IDE的區別,通常IDE包含編譯器)同時也會閱讀這篇文章的人(結構體系)來閱讀、理解、標記這篇文章。

    有學妹問過我,為什麼簡單的代碼,能實現豐富的效果。其實這取決於編譯器的強大能力。下面來簡單介紹一下,編輯器,編譯器,IDE(集成開發環境)的區別。

    • 編輯器:編輯器就是用來編輯的軟件,比如windows自帶的記事本就是一個編輯器, 記事本沒有語法高亮,不显示行號,當一段可執行代碼寫完后無法通過內置環境執行,必須手動輸入命令執行編譯等等一些弊端,所以很少有程序員會用記事本去寫代碼 , 寫代碼比較好用的編輯器軟件有vscode,vim,sublime,notepad++,emacs,atom等等 ,雖然編輯器原始功能不足,但是開發人員為了使編輯器更加友好,所以有很多內置插件可供使用,完全可以手動打造一個IDE。
    • 編譯器:簡單來說,編譯器就是將“一種語言(一般為高級語言,如c,c++,java,python等,計算機不可直接識別和執行)”翻譯為“另一種語言(一般為低級語言,低級語言即機器語言,機器語言是用二進制代碼錶示的計算機能直接識別和執行的一種機器指令的集合)”的程序。舉個例子,用Dev-C++寫好一段可執行"hello,world!"C語言代碼之後,我們要讓它在屏幕打印出來我們想要它輸出的"hello,world!",就需要通過gcc編譯器執行編譯后才能显示。其他語言同理。
    • IDE:集成開發環境,用於程序開發環境的應用程序,一般包含代碼編輯器編譯器調試器圖像用戶界面等工具。集成了代碼編寫程序分析程序編譯程序調試等功能。如 jetbrains 的用於Java開發的 IntelliJ IDEA 、用於JavaScript開發的WebStorm、用於Python開發Pycharm,微軟的 Visual Studio系列 ,IBM的Eclipse。

    二、我們該學什麼

    很多初入計算機相關專業的萌新,總是很迷茫,不知道自己該學什麼,通常是他們知道如何去學好學校開設的每一門課程,就是不知道自己該向哪些方向學習,這些方向指的是專業技能和就業方向,諸如web開發、Android/IOS開發、數據分析、人工智能、網絡安全、遊戲開發、軟件測試等等。有這種疑惑很正常,迷茫也是正常的,但我們總要讓自己了解自己所需,然後腳踏實地,一步一步去充實自己的能力。而我想做的也很簡單,就是幫助大家解除心裏的疑惑。那麼,我們開始進入正題。

    1. 我們該如何選擇適合自己的方向

    對於這個問題,其實是很難回答清楚的,因為每個人的興趣都不相同,所以就很難去站在自己的角度去回答疑問者的問題。但是,原理都是想通的,我相信我的經驗會幫助到你們。

    • 通常,學校每學期都會開設一門或多門語言(程序設計語言,下文同),那麼,喜歡一門語言,首先要愛上它的語言風格,諸如Java的嚴謹,Python的自由,總有一款適合你;其次,在學習語言的過程中,一定要了解它能幹什麼,市場環境如何,工作崗位多少等綜合因素,再決定要不要去深入這門語言,並且主攻自己感興趣的那個方向。

    • 對於學校沒有開設,但是自己又想學習的語言而言,該如何去選擇。首先,學校開設的語言基本是市場比較流行的語言,也符合市場需求,所以,完全可以在學校開設的語言中去選擇自己想要了解並學習的語言。此外,我們可以藉助 TIOBE ( TIOBE 編程社區指數是編程語言流行度的指標,該榜單每月更新一次,指數基於全球技術工程師、課程和第三方供應商的數量。包括流行的搜索引擎,如谷歌、必應、雅虎、維基百科、亞馬遜、YouTube 和百度都用於指數計算。 )去了解語言的流行程度,流行程度決定市場需求,以此來參考自己想要了解並學習的語言,在此附上2019年11月語言排名。

    2. 主流編程語言主要應用場景

    • Java

      1. 企業級應用開發: 大到全國聯網的系統,小到中小企業的應用解決方案,Java都佔有極為重要的地位 。
      2. web後端開發: JSP+Servlet+JavaBean 是一種比較流行的開發模式。
      3. 移動領域:手機遊戲。
      4. Android App開發: android 開發只用到了JAVA的語法和JAVA SE的一小部分API。
    • C

      C語言是一門基礎語言,是其他一些語言的基礎,例如MATLAB,Object-C,Lua等.同時也是學習來比較難的語言,達到精通的程度沒有3-10年左右很難,C語言沒有比較完善的開發框架,是面向過程的一門語言,講究算法跟邏輯。

      1. 科研
      2. 服務器: 網絡核心設備,如路由器、交換機、防火牆。
      3. 操作系統:類unix系統(Linux/freebsd)
      4. 嵌入式開發: 在一個特定的硬件環境上開發與構建特定的可編程軟件系統的綜合技術。
      5. 自動化控制
    • Python

      1. 圖形處理
      2. 數學處理
      3. 文本處理
      4. 數據庫編程
      5. 網絡編程
      6. 多媒體應用
      7. pymo引擎: 運行於Symbian S60V3,Symbian S60V5,Symbian 3,Android,Windows,Linux,Mac Os,Maemo,MeeGo系統上的AVG遊戲引擎。
      8. 黑客編程
      9. 網絡安全
    • C++

      1. 遊戲開發
      2. 科學計算
      3. 網絡軟件
      4. 操作系統
      5. 設備驅動程序
      6. 移動設備
      7. 嵌入式開發
      8. 科研
      9. 編譯器
    • C#

      1. web後端開發
      2. 桌面軟件開發
      3. 人工智能
      4. 遊戲開發
    • JavaScript
      唯一能用於前後端開發的語言web前端開發
      1. web前端開發
      2. node web後端開發
      3. 操作系統
      4. 後台
      5. 桌面軟件開發
      6. 混合App
      7. 小程序
    • PHP

      1. web後端開發
      2. 桌面軟件開發
      3. 命令行腳本
    • SQL

      1. 操作數據庫
    • Swift

      1. 蘋果生態系統應用開發
    • Ruby

      1. web開發
    • R

      數據科學闖天下,左手Python右手R

      1. 機器學習
      2. 數據分析
      3. 科學計算
    • Go

      1. web後端開發
      2. 高性能服務器應用

    3. 主流編程語言學習路徑(將持續更新,僅供參考)

    • JavaScript

    4. 主流編程語言入門學習書籍推薦

    語言 書籍
    C 《嗨翻C語言》
    C++ 《C++權威教程》
    Java 《Java輕鬆學》
    Python 《Python編程從入門到實戰》
    JavaScript 《JavaScript入門經典》
    PHP 《PHP編程實戰》
    SQL 《SQL基礎教程》
    Swift 《Swift編程權威指南》
    Ruby 《Ruby從入門到精通》
    R 《R語言實戰》
    Go 《Go語言聖經》

    5. 編程學習網站推薦

    網站 網址
    菜鳥教程
    W3School
    實驗樓
    猿學
    慕課網
    SegmentFault
    博客園
    GitHub
    掘金
    學習數據科學
    易百教程
    看雲

    三、總結

    通篇寫完,感覺自己也重新學到了很多,學習就是一個反覆複習的過程,每次學習都能帶給自己不一樣的收穫。希望以上內容可以給初入計算機相關專業的萌新帶來一些幫助,後面我會不斷更新和優化本文,請大家持續關注。

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

    【其他文章推薦】

    ※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

    ※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

    ※Google地圖已可更新顯示潭子電動車充電站設置地點!!

    ※帶您來看台北網站建置台北網頁設計,各種案例分享

  • Spring 框架常用語法進行總結

     

    Spring 框架常用語法進行總結:

    spring框架的二大主要的功能就是IOC和AOP。

    IOC: 控制反轉(依賴注入)

    AOP: 面向切面編程

    學習spring最好的方法就是去看官網,裏面有詳細的說明及使用原則

    介紹spring 中的註解的使用,xml配置等目前在市面上面較少。

     

    首先介紹Java自帶的元註解 (元註解就是 能註解到註解上的註解,能用在其他註解上的註解 )

    Java5.0定義了4個標準的meta-annotation類型

    @Target :

    用於描述註解的範圍,即註解在哪用。它說明了Annotation所修飾的對象範圍:Annotation可被用於 packages、types(類、接口、枚舉、Annotation類型)、類型成員(方法、構造方法、成員變量、枚舉值)、方法參數和本地變量(如循環變量、catch參數)等。取值類型(ElementType)

       CONSTRUCTOR:用於描述構造器
      FIELD:用於描述域即類成員變量
      LOCAL_VARIABLE:用於描述局部變量
      METHOD:用於描述方法
      PACKAGE:用於描述包
      PARAMETER:用於描述參數
      TYPE:用於描述類、接口(包括註解類型) 或enum聲明
      TYPE_PARAMETER:1.8版本開始,描述類、接口或enum參數的聲明
      TYPE_USE:1.8版本開始,描述一種類、接口或enum的使用聲明
      
    eg :

    public @interface Log {
      ......
    }

     

    @Retention :

    用於描述註解的生命周期,表示需要在什麼級別保存該註解,即保留的時間長短。取值類型RetentionPolicy)

       SOURCE:在源文件中有效(即源文件保留)
      CLASS:在class文件中有效(即class保留)
      RUNTIME:在運行時有效(即運行時保留)  
    eg:

    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Log {
      ......
    }

     

    @Documented :

    用於描述其它類型的annotation應該被作為被標註的程序成員的公共API,因此可以被例如javadoc此類的工具文檔化。它是一個標記註解,沒有成員。

    eg :

    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Log {
      ......
    }
    @Inherited :

    用於表示某個被標註的類型是被繼承的。如果一個使用了@Inherited修飾的annotation類型被用於一個class,則這個annotation將被用於該class的子類。

     

     

    Spring 常用的註解

    在註解配置中常用的啟動方法就是:

    <--在XML中啟用方法-->
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
    Person bean = (Person) applicationContext.getBean("person");
    System.out.println(bean);

    --------------------------------------------------------------------------
       <--在註解中啟用方法-->
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
    Person bean = applicationContext.getBean(Person.class);
    System.out.println(bean);


    getBeanNamesForType:得到當前IOC容器加載進來的bean的名稱
    String[] namesForType = applicationContext.getBeanNamesForType(Person.class);
    for (String name : namesForType) {
    System.out.println(name);
    }
    @Component

    組件,沒有明確的角色

    @Service

    在業務邏輯層使用(service層)

    @Repository

    在數據訪問層使用(dao層)

    @Controller

    在展現層使用,控制器的聲明(Controller)

    @Bean

    注入ioc容器中,默認是以方法的名稱作為注入容器裏面的名稱,需注意@bean可以不在配置類裏面使用,不過經過@Bean註解使用過的方法所在的類也會被加載到ioc容器裏面。

    //配置類==配置文件

    @Configuration

    告訴Spring這是一個配置類,用在一個類的上面,配置類

    @Configuration == <bean id=”person” class=”com.opendev.entity.Person”></bean>

    @ComponentScan

    value:指定要掃描的包,用在配置類上面,告訴程序在spring中的掃包範圍

    @ComponentScans

    掃描多個包還有提供掃包的自定義掃包規則

     

    package com.atguigu.config;

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.FilterType;
    import org.springframework.context.annotation.ComponentScan.Filter;
    import org.springframework.context.annotation.ComponentScans;

    import com.atguigu.bean.Person;

    //配置類==配置文件
    @Configuration  //告訴Spring這是一個配置類

    @ComponentScans(
    value = {
    @ComponentScan(value="com.atguigu",includeFilters = {
    /*@Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
    @Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}),*/
    @Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})
    },useDefaultFilters = false)
    }
    )
    //@ComponentScan value:指定要掃描的包
    //excludeFilters = Filter[] :指定掃描的時候按照什麼規則排除那些組件
    //includeFilters = Filter[] :指定掃描的時候只需要包含哪些組件
    //FilterType.ANNOTATION:按照註解
    //FilterType.ASSIGNABLE_TYPE:按照給定的類型;
    //FilterType.ASPECTJ:使用ASPECTJ表達式
    //FilterType.REGEX:使用正則指定
    //FilterType.CUSTOM:使用自定義規則
    public class MainConfig {

    //給容器中註冊一個Bean;類型為返回值的類型,id默認是用方法名作為id
    @Bean("person")//聲明了注入ioc容器裏面的對象為person,默認都是以方法名作為id
    public Person person01(){
    return new Person("lisi", 20);
    }
    }

     

    //類中組件統一設置。滿足當前條件,這個類中配置的所有bean註冊才能生效;

    @Conditional

    裏面需要寫上相應接口的實現類

    @Import

    導入組件,id默認是組件的全類名

    spring中bean的作用域

    默認是單實例的

    //默認是單實例的
    /**
    * ConfigurableBeanFactory#SCOPE_PROTOTYPE    
    * @see ConfigurableBeanFactory#SCOPE_SINGLETON  
    * @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST request
    * @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION sesssion
    * @return\
    * @Scope:調整作用域
    * prototype:多實例的:ioc容器啟動並不會去調用方法創建對象放在容器中。
    * 每次獲取的時候才會調用方法創建對象;
    * singleton:單實例的(默認值):ioc容器啟動會調用方法創建對象放到ioc容器中。
    * 以後每次獲取就是直接從容器(map.get())中拿,
    * request:同一次請求創建一個實例
    * session:同一個session創建一個實例
    *
    * 懶加載:
    * 單實例bean:默認在容器啟動的時候創建對象;
    * 懶加載:容器啟動不創建對象。第一次使用(獲取)Bean創建對象,並初始化;
    *
    */
    //@Scope("prototype")
    @Lazy
    @Bean("person")
    public Person person(){
    System.out.println("給容器中添加Person....");
    return new Person("張三", 25);
    }
    spring中bean 的生命周期
    /**
    * bean的生命周期:
    * bean創建---初始化----銷毀的過程
    * 容器管理bean的生命周期;
    * 我們可以自定義初始化和銷毀方法;容器在bean進行到當前生命周期的時候來調用我們自定義的初始化和銷毀方法
    *
    * 構造(對象創建)
    * 單實例:在容器啟動的時候創建對象
    * 多實例:在每次獲取的時候創建對象\
    *
    * BeanPostProcessor.postProcessBeforeInitialization
    * 初始化:
    * 對象創建完成,並賦值好,調用初始化方法。。。
    * BeanPostProcessor.postProcessAfterInitialization
    * 銷毀:
    * 單實例:容器關閉的時候
    * 多實例:容器不會管理這個bean;容器不會調用銷毀方法;
    *
    *
    * 遍歷得到容器中所有的BeanPostProcessor;挨個執行beforeInitialization,
    * 一但返回null,跳出for循環,不會執行後面的BeanPostProcessor.postProcessorsBeforeInitialization
    *
    * BeanPostProcessor原理
    * populateBean(beanName, mbd, instanceWrapper);給bean進行屬性賦值
    * initializeBean
    * {
    * applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    * invokeInitMethods(beanName, wrappedBean, mbd);執行自定義初始化
    * applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    *}
    *
    *
    *
    * 1)、指定初始化和銷毀方法;
    * 通過@Bean指定init-method和destroy-method;
    * 2)、通過讓Bean實現InitializingBean(定義初始化邏輯),
    * DisposableBean(定義銷毀邏輯);
    * 3)、可以使用JSR250;
    * @PostConstruct:在bean創建完成並且屬性賦值完成;來執行初始化方法
    * @PreDestroy:在容器銷毀bean之前通知我們進行清理工作
    * 4)、BeanPostProcessor【interface】:bean的後置處理器;
    * 在bean初始化前後進行一些處理工作;
    * postProcessBeforeInitialization:在初始化之前工作
    * postProcessAfterInitialization:在初始化之後工作
    *
    * Spring底層對 BeanPostProcessor 的使用;
    * bean賦值,注入其他組件,@Autowired,生命周期註解功能,@Async,xxx BeanPostProcessor;
    *
    *
    *
    */
    spring中的自動裝配
    /**
    * 自動裝配;
    * Spring利用依賴注入(DI),完成對IOC容器中中各個組件的依賴關係賦值;
    *
    * 1)、@Autowired:自動注入:
    * 1)、默認優先按照類型去容器中找對應的組件:applicationContext.getBean(BookDao.class);找到就賦值
    * 2)、如果找到多個相同類型的組件,再將屬性的名稱作為組件的id去容器中查找
    * applicationContext.getBean("bookDao")
    * 3)、@Qualifier("bookDao"):使用@Qualifier指定需要裝配的組件的id,而不是使用屬性名
    * 4)、自動裝配默認一定要將屬性賦值好,沒有就會報錯;
    * 可以使用@Autowired(required=false);
    * 5)、@Primary:讓Spring進行自動裝配的時候,默認使用首選的bean;
    * 也可以繼續使用@Qualifier指定需要裝配的bean的名字
    * BookService{
    * @Autowired
    * BookDao bookDao;
    * }
    *
    * 2)、Spring還支持使用@Resource(JSR250)和@Inject(JSR330)[java規範的註解]
    * @Resource:
    * 可以和@Autowired一樣實現自動裝配功能;默認是按照組件名稱進行裝配的;
    * 沒有能支持@Primary功能沒有支持@Autowired(reqiured=false);
    * @Inject:
    * 需要導入javax.inject的包,和Autowired的功能一樣。沒有required=false的功能;
    * @Autowired:Spring定義的; @Resource、@Inject都是java規範
    *
    * AutowiredAnnotationBeanPostProcessor:解析完成自動裝配功能;
    *
    * 3)、 @Autowired:構造器,參數,方法,屬性;都是從容器中獲取參數組件的值
    * 1)、[標註在方法位置]:@Bean+方法參數;參數從容器中獲取;默認不寫@Autowired效果是一樣的;都能自動裝配
    * 2)、[標在構造器上]:如果組件只有一個有參構造器,這個有參構造器的@Autowired可以省略,參數位置的組件還是可以自動從容器中獲取
    * 3)、放在參數位置:
    *
    * 4)、自定義組件想要使用Spring容器底層的一些組件(ApplicationContext,BeanFactory,xxx);
    * 自定義組件實現xxxAware;在創建對象的時候,會調用接口規定的方法注入相關組件;Aware;
    * 把Spring底層一些組件注入到自定義的Bean中;
    * xxxAware:功能使用xxxProcessor;
    * ApplicationContextAware==》ApplicationContextAwareProcessor;
    *
    *
    *
    *
    */

     

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

    【其他文章推薦】

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!