<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ## 轉發(Forward)和重定向(Redirect)的區別 **轉發是服務器行為,重定向是客戶端行為。** **轉發(Forword)** 通過RequestDispatcher對象的forward(HttpServletRequest request,HttpServletResponse response) 方法實現的。RequestDispatcher 可以通過HttpServletRequest 的 getRequestDispatcher() 方法獲得。例如下面的代碼就是跳轉到 login_success.jsp 頁面。 **重定向(Redirect)** 是利用服務器返回的狀態嗎來實現的。客戶端瀏覽器請求服務器的時候,服務器會返回一個狀 態碼。服務器通過HttpServletRequestResponse的setStatus(int status)方法設置狀態碼。如果服務器返回301或者 302,則瀏覽器會到新的網址重新請求該資源。 1. **從地址欄顯示來說**: forward是服務器請求資源,服務器直接訪問目標地址的URL,把那個URL的響應內容讀取過來, 然后把這些內容再發給瀏覽器.瀏覽器根本不知道服務器發送的內容從哪里來的,所以它的**地址欄還是原來的地址**. redirect是服務端根據邏輯,發送一個狀態碼,告訴瀏覽器重新去請求那個地址.所以**地址欄顯示的是新的URL**. 2. **從數據共享來說**: forward:轉發頁面和轉發到的頁面可以共享request里面的數據. redirect:不能共享數據. 3. **從運用地方來說**: forward:一般用于用戶**登陸**的時候,根據角色轉發到相應的模塊. redirect:一般用于用戶**注銷**登 陸時返回主頁面和跳轉到其它的網站等 4. **從效率來說**: forward:高. redirect:低. ## TCP/IP五層協議 物理層、數據鏈路層、網絡層、傳輸層、應用層 ## TCP、UDP 協議的區別 ![](https://box.kancloud.cn/fc4d9463833cb2c88eb0394696238436_1128x293.png) **UDP 在傳送數據之前不需要先建立連接**,遠地主機在收到 UDP 報文后,不需要給出任何確認。雖然 UDP 不提供可 靠交付,但在某些情況下 UDP 確是一種最有效的工作方式(一般用于即時通信),比如: **QQ 語音、 QQ 視頻 、直 播**等等 **TCP 提供面向連接的服務**。在傳送數據之前必須先建立連接,數據傳送結束后要釋放連接。 TCP 不提供廣播或多播 服務。由于 TCP 要提供可靠的,面向連接的運輸服務(TCP的可靠體現在TCP在傳遞數據之前,會有三次握手來建立 連接,而且在數據傳遞時,有確認、窗口、重傳、擁塞控制機制,在數據傳完后,還會斷開連接用來節約系統資 源),這一難以避免增加了許多開銷,如確認,流量控制,計時器以及連接管理等。這不僅使協議數據單元的首部增 大很多,還要占用許多處理機資源。**TCP 一般用于文件傳輸、發送和接收郵件、遠程登錄等場景**。 ## HTTP長連接、短連接 **在HTTP/1.0中默認使用短連接**。也就是說,客戶端和服務器每進行一次HTTP操作,就建立一次連接,任務結束就中 斷連接。當客戶端瀏覽器訪問的某個HTML或其他類型的Web頁中包含有其他的Web資源(如JavaScript文件、圖像 文件、CSS文件等),每遇到這樣一個Web資源,瀏覽器就會重新建立一個HTTP會話。 而**從HTTP/1.1起,默認使用長連接,用以保持連接特性**。使用長連接的HTTP協議,會在響應頭加入這行代碼: ``` Connection:keep-alive ``` 在使用長連接的情況下,當一個網頁打開完成后,客戶端和服務器之間用于傳輸HTTP數據的TCP連接不會關閉,客 戶端再次訪問這個服務器時,會繼續使用這一條已經建立的連接。**Keep-Alive不會永久保持連接,它有一個保持時 間**,可以在不同的服務器軟件(如Apache)中設定這個時間。**實現長連接需要客戶端和服務端都支持長連接**。 HTTP協議的長連接和短連接,實質上是TCP協議的長連接和短連接。 ## 在瀏覽器中輸入url地址到顯示主頁的過程 **總體來說分為以下幾個過程**: 1. DNS解析 (瀏覽器查找域名的IP地址) 2. TCP連接 3. 發送HTTP請求 (瀏覽器向WEB服務器發送請求) 4. 服務器處理請求并返回HTTP報文 5. 瀏覽器解析渲染頁面 6. 連接結束 ## TCP 三次握手和四次揮手 **三次握手** 三次握手的目的是建立可靠的通信信道,說到通訊,簡單來說就是數據的發送與接收,而三次握手最主要的目的就是 **雙方確認自己與對方的發送與接收是正常**的。 客戶端–發送帶有 SYN 標志的數據包–一次握手–服務端 服務端–發送帶有 SYN/ACK 標志的數據包–二次握手–客戶端 客戶端–發送帶有帶有 ACK 標志的數據包–三次握手–服務端 **四次揮手** 斷開一個 TCP 連接則需要“四次揮手”: 任何一方都可以在數據傳送結束后發出連接釋放的通知,待對方確認后進入半關閉狀態。當另一方也沒有數據再發送 的時候,則發出連接釋放通知,對方確認后就完全關閉了TCP連接。 客戶端-發送一個 FIN,用來關閉客戶端到服務器的數據傳送 服務器-收到這個 FIN,它發回一 個 ACK,確認序號為收到的序號加1 。和 SYN 一樣,一個 FIN 將占用一個序號 服務器-關閉與客戶端的連接,發送一個FIN給客戶端 客戶端-發回 ACK 報文確認,并將確認序號設置為收到序號加1 ## 為什么要使用索引 1. 通過創建唯一性索引,可以保證數據庫表中每一行數據的唯一性。 2. 可以大大加快 數據的檢索速度(大大減少的檢索的數據量), 這也是創建索引的最主要的原因。 3. 幫助服務器避免排序和臨時表 4. 將隨機IO變為順序IO 5. 可以加速表和表之間的連接,特別是在實現數據的參考完整性方面特別有意義。 ## 索引這么多優點,為什么不對表中的每一個列創建一個索引呢? 1. 當對表中的數據進行增加、刪除和修改的時候,索引也要動態的維護,這樣就降低了數據的維護速度。 2. 索引需要占物理空間,除了數據表占數據空間之外,每一個索引還要占一定的物理空間,如果要建立聚簇索 引,那么需要的空間就會更大。 3. 創建索引和維護索引要耗費時間,這種時間隨著數據量的增加而增加。 ## 索引是如何提高查詢速度的? 將無序的數據變成相對有序的數據(就像查目錄一樣) ## Mysql索引主要使用的哪兩種數據結構? 1. 哈希索引:對于哈希索引來說,底層的數據結構就是哈希表,因此在絕大多數需求為單條記錄查詢的時候,可 以選擇哈希索引,查詢性能最快;其余大部分場景,建議選擇BTree索引。 2. BTree索引:Mysql的BTree索引使用的是B樹中的B+Tree。但對于主要的兩種存儲引擎(MyISAM和InnoDB) 的實現方式是不同的。 ## 什么是覆蓋索引? 如果一個索引包含(或者說覆蓋)所有需要查詢的字段的值,我們就稱 之為“覆蓋索引”。我們知道在InnoDB存儲引 擎中,如果不是主鍵索引,葉子節點存儲的是主鍵+列值。最終還是要“回表”,也就是要通過主鍵再查找一次,這樣就 會比較慢。覆蓋索引就是把要查詢出的列和索引是對應的,不做回表操作! ## MyISAM與InnoDB MyISAM更適合讀密集的表,而InnoDB更適合寫密集的表。 在數據庫做主從分離的情況下,經常選擇MyISAM作 為主庫的存儲引擎。 一般來說,如果需要事務支持,并且有較高的并發讀取頻率(MyISAM的表鎖的粒度太大,所以當該表寫并發量較高 時,要等待的查詢就會很多了),InnoDB是不錯的選擇。如果你的數據量很大(MyISAM支持壓縮特性可以減少磁盤 的空間占用),而且不需要支持事務時,MyISAM是最好的選擇。 **主要區別:** 1. MyISAM是非事務安全型的,而InnoDB是事務安全型的。 2. MyISAM鎖的粒度是表級,而InnoDB支持行級鎖定。 3. MyISAM支持全文類型索引,而InnoDB不支持全文索引。 4. MyISAM相對簡單,所以在效率上要優于InnoDB,小型應用可以考慮使用MyISAM。 5. MyISAM表是保存成文件的形式,在跨平臺的數據轉移中使用MyISAM存儲會省去不少的麻煩。 6. InnoDB表比MyISAM表更安全,可以在保證數據不會丟失的情況下,切換非事務表到事務表(alter table tablename type=innodb)。 7. count運算上的區別: 因為MyISAM緩存有表meta-data(行數等),因此在做COUNT(*)時對于一個結構很好 的查詢是不需要消耗多少資源的。而對于InnoDB來說,則沒有這種緩存 8. 是否支持事務和崩潰后的安全恢復: MyISAM 強調的是性能,每次查詢具有原子性,其執行數度比InnoDB類型 更快,但是不提供事務支持。但是InnoDB 提供事務支持事務,外部鍵等高級數據庫功能。 具有事務 (commit)、回滾(rollback)和崩潰修復能力(crash recovery capabilities)的事務安全(transaction-safe (ACID compliant))型表。 9. 是否支持外鍵: MyISAM不支持,而InnoDB支持。 **應用場景:** 1. MyISAM管理非事務表。它提供高速存儲和檢索,以及全文搜索能力。如果應用中需要執行大量的SELECT查詢,那么MyISAM是更好的選擇。 2. InnoDB用于事務處理應用程序,具有眾多特性,包括ACID事務支持。如果應用中需要執行大量的INSERT或 UPDATE操作,則應該使用InnoDB,這樣可以提高多用戶并發操作的性能。 ## 當MySQL單表記錄數過大時,一些常見的優化措施 當MySQL單表記錄數過大時,數據庫的CRUD性能會明顯下 降 1. 限定數據的范圍: 務必禁止不帶任何限制數據范圍條件的查詢語句。比如:我們當用戶在查詢訂單歷史的時 候,我們可以控制在一個月的范圍內。; 2. 讀/寫分離: 經典的數據庫拆分方案,主庫負責寫,從庫負責讀; 3. 垂直分區: 根據數據庫里面數據表的相關性進行拆分。 例如,用戶表中既有用戶的登錄信息又有用戶的基本信 息,可以將用戶表拆分成兩個單獨的表,甚至放到單獨的庫做分庫。簡單來說垂直拆分是指數據表列的拆分, 把一張列比較多的表拆分為多張表。 如下圖所示,這樣來說大家應該就更容易理解了。 垂直拆分的優點: 可以使得行數據變小,在查詢時減少讀取的Block數,減少I/O次數。此外,垂直分區可以簡 化表的結構,易于維護。垂直拆分的缺點: 主鍵會出現冗余,需要管理冗余列,并會引起Join操作,可以通過 在應用層進行Join來解決。此外,垂直分區會讓事務變得更加復雜; 4. 水平分區: 保持數據表結構不變,通過某種策略存儲數據分片。這樣每一片數據分散到不同的表或者庫中,達 到了分布式的目的。 水平拆分可以支撐非常大的數據量。 水平拆分是指數據表行的拆分,表的行數超過200萬 行時,就會變慢,這時可以把一張的表的數據拆成多張表來存放。舉個例子:我們可以將用戶信息表拆分成多 個用戶信息表,這樣就可以避免單一表數據量過大對性能造成影響。 水平拆分可以支持非常大的數據量。需要注意的一點是:分表僅僅是解決了單一表數據過大的問題,但由于表的 數據還是在同一臺機器上,其實對于提升MySQL并發能力沒有什么意義,所以 水平拆分最好分庫 。水平拆分能 夠 支持非常大的數據量存儲,應用端改造也少,但 分片事務難以解決 ,跨界點Join性能較差,邏輯復雜。 ## 進程與線程的區別是什么? 線程與進程相似,但線程是一個比進程更小的執行單位。一個進程在其執行的過程中可以產生多個線程。與進程不同 的是同類的多個線程共享同一塊內存空間和一組系統資源,所以系統在產生一個線程,或是在各個線程之間作切換工 作時,負擔要比進程小得多,也正因為如此,**線程也被稱為輕量級進程**。另外,也正是因為共享資源,所以線程中執 行時一般都要進行同步和互斥。總的來說,進程和線程的主要差別在于它們是**不同的操作系統資源管理方式**。 ## 進程間的幾種通信方式 1. 管道(pipe):管道是一種半雙工的通信方式,數據只能單向流動,而且只能在具有血緣關系的進程間使用。 進程的血緣關系通常指父子進程關系。管道分為pipe(無名管道)和fifo(命名管道)兩種,有名管道也是半雙 工的通信方式,但是它允許無親緣關系進程間通信。 2. 信號量(semophore):信號量是一個計數器,可以用來控制多個進程對共享資源的訪問。它通常作為一種鎖 機制,防止某進程正在訪問共享資源時,其他進程也訪問該資源。因此,主要作為進程間以及同一進程內不同 線程之間的同步手段。 3. 消息隊列(message queue):消息隊列是由消息組成的鏈表,存放在內核中 并由消息隊列標識符標識。消 息隊列克服了信號傳遞信息少,管道只能承載無格式字節流以及緩沖區大小受限等缺點。消息隊列與管道通信 相比,其優勢是對每個消息指定特定的消息類型,接收的時候不需要按照隊列次序,而是可以根據自定義條件 接收特定類型的消息。 4. 信號(signal):信號是一種比較復雜的通信方式,用于通知接收進程某一事件已經發生。 5. 共享內存(shared memory):共享內存就是映射一段能被其他進程所訪問的內存,這段共享內存由一個進 程創建,但多個進程都可以訪問,**共享內存是最快的IPC方式**,它是針對其他進程間的通信方式運行效率低而專 門設計的。**它往往與其他通信機制,如信號量配合使用,來實現進程間的同步和通信**。 6. 套接字(socket):socket,即套接字是一種通信機制,憑借這種機制,客戶/服務器(即要進行通信的進程) 系統的開發工作既可以在本地單機上進行,也可以跨網絡進行。也就是說它可以讓不在同一臺計算機但通過網 絡連接計算機上的進程進行通信。也因為這樣,套接字明確地將客戶端和服務器區分開來。 ## 線程間的幾種通信方式 線程間通信的主要目的是用于**線程同步**,所以線程沒有像進程通信中用于**數據交換的**通信機制。 1. 鎖機制 互斥鎖:提供了以排它方式阻止數據結構被并發修改的方法。 讀寫鎖:允許多個線程同時讀共享數據,而對寫操作互斥。 條件變量:可以以原子的方式阻塞進程,直到某個特定條件為真為止。對條件測試是在互斥鎖的保護下進行 的。條件變量始終與互斥鎖一起使用。 2. 信號量機制:包括無名線程信號量與有名線程信號量 3. 信號機制:類似于進程間的信號處理。 ## 單例模式 對于頻繁使用的對象,可以省略創建對象所花費的時間,這對于那些重量級對象而言,是非常可觀的一筆系統 開銷; 由于 new 操作的次數減少,因而對系統內存的使用頻率也會降低,這將減輕 GC 壓力,縮短 GC 停頓時間。 **單線程:** * 餓漢式 ``` public class HungryMode { private static HungryMode sHungryMode = new HungryMode(); private HungryMode() { } public static HungryMode getInstance(){ return sHungryMode; } } ``` * 懶漢式 ``` public class LazyMode { private static LazyMode sLazyMode; private LazyMode() { } public static LazyMode getInstance(){ if (sLazyMode == null) { sLazyMode = new LazyMode(); } return sLazyMode; } } ``` **多線程:** * 雙重校驗機制 ``` /** * 多線程的單例模式,使用雙重校驗機制 */ public class DoubleCheckMode { private volatile static DoubleCheckMode sDoubleCheckMode ; public DoubleCheckMode() { } public static DoubleCheckMode getInstance() { if (sDoubleCheckMode == null) synchronized (DoubleCheckMode.class) { if (sDoubleCheckMode == null) { sDoubleCheckMode = new DoubleCheckMode(); } } return sDoubleCheckMode; } } ``` * 靜態內部類 ``` /** * 靜態內部類的方式實現單例,可以保證多線程的對象唯一性,還有提升性能,不用同步鎖機制 */ public class InnerStaticMode { private static class SingleTonHolder { public static InnerStaticMode sInnerStaticMode = new InnerStaticMode(); } public static InnerStaticMode getInstance(){ return SingleTonHolder.sInnerStaticMode; } } ``` * 枚舉 枚舉實現單例,不推薦在Android平臺使用,因為內存消耗會其他方式多一些,Android官方也不推薦枚舉,Android平臺推薦雙重校驗或者靜態內部類單例,現在的Android開發環境jdk一般都大于1.5了。所以volatile的問題不必擔心。Java平臺開發的Effective Java一書中推薦使用枚舉實現單例,可以保證效率,而且還能解決反序列化創建新對象的問題。 ``` /** * 利用枚舉的方式實現單例,Android不推薦 */ public enum EnumMode { INSTANCE; private int id; public int getId() { return id; } public void setId(int id) { this.id = id; } } ``` ## Spring、SpringMVC和Springboot的區別 Spring?最初利用“工廠模式”(DI,IoC)和“代理模式”(AOP)解耦應用組件。大家覺得挺好用,于是按照這種模式搞了一個?MVC框架(一些用Spring?解耦的組件),用開發?web?應用(?SpringMVC?)。然后發現每次開發都寫很多樣板代碼,為了簡化工作流程,于是開發出了一些“懶人整合包”(starter),這套就是?Spring Boot。 所以,用最簡練的語言概括就是: * Spring?是一個“引擎”; * Spring MVC?是基于Spring的一個?MVC?框架; * Spring Boot?是基于Spring4的條件注冊的一套快速開發整合包。 **SpringBoot和SpringCloud區別** * Spring boot 是 Spring 的一套快速配置腳手架,可以基于spring boot 快速開發單個微服務; Spring Cloud是一個基于Spring Boot實現的云應用開發工具 * Spring boot可以離開Spring Cloud獨立使用開發項目,但是Spring Cloud離不開Spring boot,屬于依賴的關系 ## Spring的bean 在 Spring 中,那些組成應用程序的主體及由 Spring IOC 容器所管理的對象,被稱之為 bean。簡單地講,**bean 就 是由 IOC 容器初始化、裝配及管理的對象**,除此之外,bean 就與應用程序中的其他對象沒有什么區別了。而 bean 的定義以及 bean 相互間的依賴關系將通過配置元數據來描述。 **Spring中的bean默認都是單例的**,這些單例Bean在多線程程序下如何保證線程安全呢? 例如對于Web應用來說, Web容器對于每個用戶請求都創建一個單獨的Sevlet線程來處理請求,引入Spring框架之后,每個Action都是單例 的,那么對于Spring托管的單例Service Bean,如何保證其安全呢? Spring的單例是基于BeanFactory也就是Spring 容器的,單例Bean在此容器內只有一個,Java的單例是基于 JVM,每個 JVM 內只有一個實例。 ## Spring AOP IOC 實現原理 **IOC: 控制反轉也叫依賴注入**。**IOC利用java反射機制,AOP利用代理模式**。IOC 概念看似很抽象,但是很容易理解。 說簡單點就是將對象交給容器管理,你只需要在spring配置文件中配置對應的bean以及設置相關的屬性,讓spring 容器來生成類的實例對象以及管理對象。在spring容器啟動的時候,spring會把你在配置文件中配置的bean都初始 化好,然后在你需要調用的時候,就把它已經初始化好的那些bean分配給你需要調用這些bean的類。 **AOP: 面向切面編程**。(Aspect-Oriented Programming) 。AOP可以說是對OOP的補充和完善。OOP引入封裝、 繼承和多態性等概念來建立一種對象層次結構,用以模擬公共行為的一個集合。實現AOP的技術,主要分為兩大類: 一是采用**動態代理技術**,**利用截取消息的方式,對該消息進行裝飾,以取代原有對象行為的執行**;二是采用**靜態織入 的方式**,**引入特定的語法創建“方面”**,從而使得編譯器可以在編譯期間織入有關“方面”的代碼,屬于靜態代理。 ## Spring MVC原理 客戶端發送請求-> 前端控制器 DispatcherServlet 接受客戶端請求 -> 找到處理器映射 HandlerMapping 解析請求對 應的 Handler-> HandlerAdapter 會根據 Handler 來調用真正的處理器開處理請求,并處理相應的業務邏輯 -> 處理 器返回一個模型視圖 ModelAndView -> 視圖解析器進行解析 -> 返回一個視圖對象->前端控制器 DispatcherServlet 渲染數據(Model)->將得到視圖對象返回給用戶 ## Spring事務隔離級別 spring(數據庫)事務隔離級別分為四種(級別遞減): Mysql 默認采用的 REPEATABLE_READ隔離級別 Oracle 默認采用的 READ_COMMITTED隔離級別 1. Serializable (串行化):最嚴格的級別,事務串行執行,資源消耗最大; 2. REPEATABLE READ(重復讀) :保證了一個事務不會修改已經由另一個事務讀取但未提交(回滾)的數據。避免了“臟讀取”和“不可重復讀取”的情況,但不能避免“幻讀”,但是帶來了更多的性能損失。 3. READ COMMITTED (提交讀):大多數主流數據庫的默認事務等級,保證了一個事務不會讀到另一個并行事務已修改但未提交的數據,避免了“臟讀取”,但不能避免“幻讀”和“不可重復讀取”。該級別適用于大多數系統。 4. Read Uncommitted(未提交讀) :事務中的修改,即使沒有提交,其他事務也可以看得到,會導致“臟讀”、“幻讀”和“不可重復讀取”。 **臟讀、不可重復讀、幻讀:** **臟讀**:所謂的臟讀,其實就是讀到了別的事務回滾前的臟數據。比如事務B執行過程中修改了數據X,在未提交前,事務A讀取了X,而事務B卻回滾了,這樣事務A就形成了臟讀。 也就是說,當前事務讀到的數據是別的事務想要修改成為的但是沒有修改成功的數據。 **不可重復讀**:事務A首先讀取了一條數據,然后執行邏輯的時候,事務B將這條數據改變了,然后事務A再次讀取的時候,發現數據不匹配了,就是所謂的不可重復讀了。 也就是說,當前事務先進行了一次數據讀取,然后再次讀取到的數據是別的事務修改成功的數據,導致兩次讀取到的數據不匹配,也就照應了不可重復讀的語義。 **幻讀**:事務A首先根據條件索引得到N條數據,然后事務B改變了這N條數據之外的M條或者增添了M條符合事務A搜索條件的數據,導致事務A再次搜索發現有N+M條數據了,就產生了幻讀。 也就是說,當前事務讀第一次取到的數據比后來讀取到數據條目少。 ## Nginx Nginx是一款輕量級的Web 服務器/反向代理服務器及電子郵件(IMAP/POP3)代理服務器。 Nginx 主要提供反向代 理、負載均衡、動靜分離(靜態資源服務)等服務。 **反向代理** 正向代理:某些情況下,代理我們用戶去訪問服務器,需要用戶手動的設置代理服務器的ip和端口號。正向代 理比較常見的一個例子就是 VPN了。 反向代理: 是用來代理服務器的,代理我們要訪問的目標服務器。代理服務器接受請求,然后將請求轉發給內 部網絡的服務器,并將從服務器上得到的結果返回給客戶端,此時代理服務器對外就表現為一個服務器。 所以,簡單的理解,就是正向代理是為客戶端做代理,代替客戶端去訪問服務器,而反向代理是為服務器做代理,代 替服務器接受客戶端請求。 **負載均衡** 在**高并發**情況下需要使用,其原理就是將并發請求分攤到多個服務器執行,減輕每臺服務器的壓力,多臺服務器(集 群)共同完成工作任務,從而提高了數據的吞吐量。 Nginx支持的weight輪詢(默認)、ip_hash、fair、url_hash這四種負載均衡調度算法,感興趣的可以自行查閱。 負載均衡相比于反向代理更側重的時將請求分擔到多臺服務器上去,所以談論負載均衡只有在提供某服務的服務器大 于兩臺時才有意義。 **動靜分離** 動靜分離是讓動態網站里的動態網頁根據一定規則把不變的資源和經常變的資源區分開來,動靜資源做好了拆分以 后,我們就可以根據靜態資源的特點將其做緩存操作,這就是網站靜態化處理的核心思路。 **優點** 1. 高并發、高性能(這是其他web服務器不具有的) 2. 可擴展性好(模塊化設計,第三方插件生態圈豐富) 3. 高可靠性(可以在服務器行持續不間斷的運行數年) 4. 熱部署(這個功能對于 Nginx 來說特別重要,熱部署指可以在不停止 Nginx服務的情況下升級 Nginx) 5. BSD許可證(意味著我們可以將源代碼下載下來進行修改然后使用自己的版本) **主要組成部分** * Nginx 二進制可執行文件:由各模塊源碼編譯出一個文件 * Nginx.conf 配置文件:控制Nginx 行為 * acess.log 訪問日志: 記錄每一條HTTP請求信息 * error.log 錯誤日志:定位問題 ## 列舉幾種消息隊列 * ActiveMQ * RabbitMQ 微秒級,延遲是最低的 * RocketMQ 分布式架構 * Kafaka 分布式架構 ## Arraylist 與 LinkedList 有什么不同 1. 是否保證線程安全: ArrayList 和 LinkedList 都是不同步的,也就是不保證線程安全; 2. 底層數據結構: Arraylist 底層使用的是Object數組;LinkedList 底層使用的是雙向鏈表數據結構(注意雙向 鏈表和雙向循環鏈表的區別:); 3. 插入和刪除是否受元素位置的影響: ① ArrayList 采用數組存儲,所以插入和刪除元素的時間復雜度受元素 位置的影響。 比如:執行add(E e) 方法的時候, ArrayList 會默認在將指定的元素追加到此列表的末尾,這種 情況時間復雜度就是O(1)。但是如果要在指定位置 i 插入和刪除元素的話( add(int index, E element) )時 間復雜度就為 O(n-i)。因為在進行上述操作的時候集合中第 i 和第 i 個元素之后的(n-i)個元素都要執行向后位/向 前移一位的操作。 ② LinkedList 采用鏈表存儲,所以插入,刪除元素時間復雜度不受元素位置的影響,都是 近似 O(1)而數組為近似 O(n)。 4. 是否支持快速隨機訪問: LinkedList 不支持高效的隨機元素訪問,而 ArrayList 支持。快速隨機訪問就是通 過元素的序號快速獲取元素對象(對應于get(int index) 方法)。 5. 內存空間占用: ArrayList的空 間浪費主要體現在在list列表的結尾會預留一定的容量空間,而LinkedList的空 間花費則體現在它的每一個元素都需要消耗比ArrayList更多的空間(因為要存放直接后繼和直接前驅以及數 據)。 ## 紅黑樹 紅黑樹屬于(自)平衡二叉樹,紅黑樹就是為了解決二叉查找樹的缺陷,因為二叉查找樹在某些情況下會退化成一個線性結構。 1. 每個節點非紅即黑; 2. 根節點總是黑色的; 3. 每個葉子節點都是黑色的空節點(NIL節點); 4. 如果節點是紅色的,則它的子節點必須是黑色的(反之不一定); 5. 從根節點到葉節點或空子節點的每條路徑,必須包含相同數目的黑色節點(即相同的黑色高度) ## Object類的常見方法總結 ``` public final native Class<?> getClass()//native方法,用于返回當前運行時對象的Class對象,使用了 final關鍵字修飾,故不允許子類重寫。 public native int hashCode() //native方法,用于返回對象的哈希碼,主要使用在哈希表中,比如JDK中的 HashMap。 public boolean equals(Object obj)//用于比較2個對象的內存地址是否相等,String類對該方法進行了重寫用 戶比較字符串的值是否相等。 protected native Object clone() throws CloneNotSupportedException//naitive方法,用于創建并返 回當前對象的一份拷貝。一般情況下,對于任何對象 x,表達式 x.clone() != x 為true, x.clone().getClass() == x.getClass() 為true。Object本身沒有實現Cloneable接口,所以不重寫clone 方法并且進行調用的話會發生CloneNotSupportedException異常。 public String toString()//返回類的名字@實例的哈希碼的16進制的字符串。建議Object所有的子類都重寫這個 方法。 public final native void notify()//native方法,并且不能重寫。喚醒一個在此對象監視器上等待的線程(監 視器相當于就是鎖的概念)。如果有多個線程在等待只會任意喚醒一個。 public final native void notifyAll()//native方法,并且不能重寫。跟notify一樣,唯一的區別就是會喚 醒在此對象監視器上等待的所有線程,而不是一個線程。 public final native void wait(long timeout) throws InterruptedException//native方法,并且不 能重寫。暫停線程的執行。注意:sleep方法沒有釋放鎖,而wait方法釋放了鎖 。timeout是等待時間。 public final void wait(long timeout, int nanos) throws InterruptedException//多了nanos參 數,這個參數表示額外時間(以毫微秒為單位,范圍是 0-999999)。 所以超時的時間還需要加上nanos毫秒。 public final void wait() throws InterruptedException//跟之前的2個wait方法一樣,只不過該方法一直 等待,沒有超時時間這個概念 protected void finalize() throws Throwable { }//實例被垃圾回收器回收的時候觸發的操作 ``` ## hashCode()與equals() 1. 如果兩個對象相等,則hashcode一定也是相同的 2. 兩個對象相等,對兩個對象分別調用equals方法都返回true 3. 兩個對象有相同的hashcode值,它們也不一定是相等的 4. 因此,equals方法被覆蓋過,則hashCode方法也必須被覆蓋 5. hashCode()的默認行為是對堆上的對象產生獨特值。如果沒有重寫hashCode(),則該class的兩個對象無論如何 都不會相等(即使這兩個對象指向相同的數據) ## ==與equals **== **: 它的作用是判斷兩個對象的地址是不是相等。即,判斷兩個對象是不是同一個對象。(基本數據類型==比較的是 值,引用數據類型==比較的是內存地址) **equals()** : 它的作用也是判斷兩個對象是否相等。但它一般有兩種使用情況: 情況1:類沒有覆蓋equals()方法。則通過equals()比較該類的兩個對象時,等價于通過“==”比較這兩個對象。 情況2:類覆蓋了equals()方法。一般,我們都覆蓋equals()方法來兩個對象的內容相等;若它們的內容相等, 則返回true(即,認為這兩個對象相等)。 String中的equals方法是被重寫過的,因為object的equals方法是比較的對象的內存地址,而String的equals方 法比較的是對象的值。 當創建String類型的對象時,虛擬機會在常量池中查找有沒有已經存在的值和要創建的值相同的對象,如果有就 把它賦給當前引用。如果沒有就在常量池中重新創建一個String對象。 ## synchronized 和 ReenTrantLock 的區別 1. 兩者都是可重入鎖 兩者都是可重入鎖。“可重入鎖”概念是:自己可以再次獲取自己的內部鎖。比如一個線程獲得了某個對象的鎖,此時 這個對象鎖還沒有釋放,當其再次想要獲取這個對象的鎖的時候還是可以獲取的,如果不可鎖重入的話,就會造成死 鎖。同一個線程每次獲取鎖,鎖的計數器都自增1,所以要等到鎖的計數器下降為0時才能釋放鎖。 2. synchronized 依賴于 JVM 而 ReenTrantLock 依賴于 API synchronized 是依賴于 JVM 實現的,前面我們也講到了 虛擬機團隊在 JDK1.6 為 synchronized 關鍵字進行了很多 優化,但是這些優化都是在虛擬機層面實現的,并沒有直接暴露給我們。ReenTrantLock 是 JDK 層面實現的(也就 是 API 層面,需要 lock() 和 unlock 方法配合 try/finally 語句塊來完成),所以我們可以通過查看它的源代碼,來看 它是如何實現的。 3. ReenTrantLock 比 synchronized 增加了一些高級功能 ReenTrantLock提供了一種能夠中斷等待鎖的線程的機制,通過lock.lockInterruptibly()來實現這個機制。也 就是說正在等待的線程可以選擇放棄等待,改為處理其他事情。 ReenTrantLock可以指定是公平鎖還是非公平鎖。而synchronized只能是非公平鎖。所謂的公平鎖就是先等 待的線程先獲得鎖。 synchronized關鍵字與wait()和notify/notifyAll()方法相結合可以實現等待/通知機制,ReentrantLock類需要借助于Condition接口與newCondition() 方法。 4. 兩者的性能已經相差無幾 在JDK1.6之前,synchronized 的性能是比 ReenTrantLock 差很多。JDK1.6之后,性能已經不是選擇synchronized和 ReenTrantLock的影響因素了! ## 線程池 * 降低資源消耗。 通過重復利用已創建的線程降低線程創建和銷毀造成的消耗。 * 提高響應速度。 當任務到達時,任務可以不需要的等到線程創建就能立即執行。 * 提高線程的可管理性。 線程是稀缺資源,如果無限制的創建,不僅會消耗系統資源,還會降低系統的穩定性, 使用線程池可以進行統一的分配,調優和監控。 **Java 主要提供了下面4種線程池** **FixedThreadPool**: 該方法返回一個固定線程數量的線程池。該線程池中的線程數量始終不變。當有一個新的 任務提交時,線程池中若有空閑線程,則立即執行。若沒有,則新的任務會被暫存在一個任務隊列中,待有線 程空閑時,便處理在任務隊列中的任務。 **SingleThreadExecutor**: 方法返回一個只有一個線程的線程池。若多余一個任務被提交到該線程池,任務會 被保存在一個任務隊列中,待線程空閑,按先入先出的順序執行隊列中的任務。 **CachedThreadPool**: 該方法返回一個可根據實際情況調整線程數量的線程池。線程池的線程數量不確定,但 若有空閑線程可以復用,則會優先使用可復用的線程。若所有線程均在工作,又有新的任務提交,則會創建新 的線程處理任務。所有線程在當前任務執行完畢后,將返回線程池進行復用。 **ScheduledThreadPoolExecutor**: 主要用來在給定的延遲后運行任務,或者定期執行任務。 ScheduledThreadPoolExecutor又分為:ScheduledThreadPoolExecutor(包含多個線程)和 SingleThreadScheduledExecutor (只包含一個線程)兩種。 **各種線程池的適用場景介紹** **FixedThreadPool**: 適用于為了滿足資源管理需求,而需要限制當前線程數量的應用場景。它適用于負載比較 重的服務器; **SingleThreadExecutor**: 適用于需要保證順序地執行各個任務并且在任意時間點,不會有多個線程是活動的 應用場景。 **CachedThreadPool**: 適用于執行很多的短期異步任務的小程序,或者是負載較輕的服務器; **ScheduledThreadPoolExecutor**: 適用于需要多個后臺執行周期任務,同時為了滿足資源管理需求而需要限 制后臺線程的數量的應用場景, **SingleThreadScheduledExecutor**: 適用于需要單個后臺線程執行周期任務,同時保證順序地執行各個任務 的應用場景。 **創建的線程池的方式** 使用 Executors 創建:在《阿里巴巴Java開發手冊》中強制線程池不允許使用 Executors 去創建。 ThreadPoolExecutor的構造函數創建:創建的同時,給 BlockQueue 指定容 量 使用開源類庫:pache和guava ## Dubbo **什么是 Dubbo** 一款高性能、輕量級的開源Java RPC 框架,它提供了三大核心能力:面向 接口的遠程方法調用,智能容錯和負載均衡,以及服務自動注冊和發現。簡單來說 Dubbo 是一個分布式服務框架, 致力于提供高性能和透明化的RPC遠程服務調用方案,以及SOA服務治理方案。 RPC(Remote Procedure Call)—遠程過程調用,它是一種通過網絡從遠程計算機程序上請求服務,而不需要了解 底層網絡技術的協議。 **Dubbo的優點** 1. 負載均衡——同一個服務部署在不同的機器時該調用那一臺機器上的服務 2. 服務調用鏈路生成——隨著系統的發展,服務越來越多,服務間依賴關系變得錯蹤復雜,甚至分不清哪個應用 要在哪個應用之前啟動,架構師都不能完整的描述應用的架構關系。Dubbo 可以為我們解決服務之間互相是如 何調用的。 3. 服務訪問壓力以及時長統計、資源調度和治理——基于訪問壓力實時管理集群容量,提高集群利用率。 4. 服務降級——某個服務掛掉之后調用備用服務 **Dubbo的架構** ![](https://box.kancloud.cn/c24d0eb6281bec8ad12b13bbde6f878b_772x522.png) * Provider: 暴露服務的服務提供方 * Consumer: 調用遠程服務的服務消費方 * Registry: 服務注冊與發現的注冊中心 * Monitor: 統計服務的調用次數和調用時間的監控中心 * Container: 服務運行容器 調用關系說明: 1. 服務容器負責啟動,加載,運行服務提供者。 2. 服務提供者在啟動時,向注冊中心注冊自己提供的服務。 3. 服務消費者在啟動時,向注冊中心訂閱自己所需的服務。 4. 注冊中心返回服務提供者地址列表給消費者,如果有變更,注冊中心將基于長連接推送變更數據給消費者。 5. 服務消費者,從提供者地址列表中,基于軟負載均衡算法,選一臺提供者進行調用,如果調用失敗,再選另一 臺調用。 6. 服務消費者和提供者,在內存中累計調用次數和調用時間,定時每分鐘發送一次統計數據到監控中心。 重要知識點總結: 注冊中心負責服務地址的注冊與查找,相當于目錄服務,服務提供者和消費者只在啟動時與注冊中心交互,注 冊中心不轉發請求,壓力較小 監控中心負責統計各服務調用次數,調用時間等,統計先在內存匯總后每分鐘一次發送到監控中心服務器,并 以報表展示 注冊中心,服務提供者,服務消費者三者之間均為長連接,監控中心除外 注冊中心通過長連接感知服務提供者的存在,服務提供者宕機,注冊中心將立即推送事件通知消費者 注冊中心和監控中心全部宕機,不影響已運行的提供者和消費者,消費者在本地緩存了提供者列表 注冊中心和監控中心都是可選的,服務消費者可以直連服務提供者 服務提供者無狀態,任意一臺宕掉后,不影響使用 服務提供者全部宕掉后,服務消費者應用將無法使用,并無限次重連等待服務提供者恢復 **zookeeper宕機與dubbo直連** 在實際生產中,假如zookeeper注冊中心宕掉,一段時間內服務消費方還是能夠調用提供方的服務的,實際上它使用 的本地緩存進行通訊,這只是dubbo健壯性的一種提現。注冊中心負責服務地址的注冊與查找,相當于目錄服務,服務提供者和消費者只在啟動時與注冊中 心交互,注冊中心不轉發請求,壓力較小。所以,我們可以完全可以繞過注冊中心——采用 dubbo 直連 ,即在服務 消費方配置服務提供方的位置信息。 ## 消息隊列 **概念** 我們可以把消息隊列比作是一個存放消息的容器,當我們需要使用消息的時候可以取出消息供自己使用。消息隊 列是**分布式系統中重要的組件**,使用消息隊列主要是為了通過異步處理**提高系統性能和削峰、降低系統耦合性**。目前 使用較多的消息隊列有ActiveMQ,RabbitMQ,Kafka,RocketMQ。 **優點** 削峰——即通過異步處理,將短時間高并發產生的事 務消息存儲在消息隊列中,從而削平高峰期的并發事務。 舉例:在電子商務一些秒殺、促銷活動中,合理使用消息 隊列可以有效抵御促銷活動剛開始大量訂單涌入對系統的沖擊。因為用戶請求數據寫入消息隊列之后就立即返回給用戶了,但是請求數據在后續的業務校驗、寫數據庫等操作中 可能失敗。因此使用消息隊列進行異步處理之后,需要適當修改業務流程進行配合。 降低耦合——不要認為消息隊列只能利用發布-訂閱模式工作,只不過**在解耦這個特定業務環境下是使用發布-訂閱模式的**。 除了發布-訂閱模式,還有點對點訂閱模式(一個消息只有一個消費者),我們比較常用的是發布-訂閱模式。 另外, 這兩種消息模型是 JMS 提供的,AMQP 協議還提供了 5 種消息模型。 **使用消息隊列帶來的一些問題** * 系統可用性降低: 系統可用性在某種程度上降低,為什么這樣說呢?在加入MQ之前,你不用考慮消息丟失或 者說MQ掛掉等等的情況,但是,引入MQ之后你就需要去考慮了! * 系統復雜性提高: 加入MQ之后,你需要保證消息沒有被重復消費、處理消息丟失的情況、保證消息傳遞的順 序性等等問題! * 一致性問題: 上面講了消息隊列可以實現異步,消息隊列帶來的異步確實可以提高系統響應速度。但是,萬 一消息的真正消費者并沒有正確消費消息怎么辦?這樣就會導致數據不一致的情況了! **JMS VS AMQP** **JMS**(JAVA Message Service,java消息服務)是java的消息服務,JMS的客戶端之間可以通過JMS服務進行異步的 消息傳輸。JMS(JAVA Message Service,Java消息服務)API是一個消息服務的標準或者說是規范,允許應用程序 組件基于JavaEE平臺創建、發送、接收和讀取消息。它使分布式通信耦合度更低,消息服務更加可靠以及異步性。 ActiveMQ 就是基于 JMS 規范實現的。 JMS兩種消息模型:點到點(P2P)模型、發布/訂閱(Pub/Sub)模型 **AMQP**,即Advanced Message Queuing Protocol,一個提供統一消息服務的應用層標準 高級消息隊列協議(二 進制應用層協議),是應用層協議的一個開放標準,為面向消息的中間件設計,兼容 JMS。基于此協議的客戶端與消 息中間件可傳遞消息,并不受客戶端/中間件同產品,不同的開發語言等條件的限制。 RabbitMQ 就是基于 AMQP 協議實現的。 AMQP提供了五種消息模型:①direct exchange;②fanout exchange;③topic change;④headers exchange;⑤system exchange。本質來講,后四種和JMS的 pub/sub模型沒有太大差別,僅是在路由機制上做了更詳細的劃分。 **對比:** * AMQP 為消息定義了線路層(wire-level protocol)的協議,而JMS所定義的是API規范。在 Java 體系中,多個 client均可以通過JMS進行交互,不需要應用修改代碼,但是其對跨平臺的支持較差。而AMQP天然具有跨平 臺、跨語言特性。 * JMS 支持TextMessage、MapMessage 等復雜的消息類型;而 AMQP 僅支持 byte[] 消息類型(復雜的類型可 序列化后發送)。 * 由于Exchange 提供的路由算法,AMQP可以提供多樣化的路由方式來傳遞消息到消息隊列,而 JMS 僅支持 隊 列 和 主題/訂閱 方式兩種。 # Spring Boot ## 什么是 Spring Boot Spring Boot 是 Spring 開源組織下的子項目,是 Spring 組件一站式解決方案,主要是簡化了使用 Spring 的難度,簡省了繁重的配置,提供了各種啟動器,開發者能快速上手。 ## 為什么要用 Spring Boot Spring Boot 優點非常多,如: * 獨立運行 * 簡化配置 * 自動配置 * 無代碼生成和XML配置 * 應用監控 * 上手容易 ## Spring Boot 的核心配置文件有哪幾個?它們的區別是什么 Spring Boot 的核心配置文件是 application 和 bootstrap 配置文件。 application 配置文件這個容易理解,主要用于 Spring Boot 項目的自動化配置。 bootstrap 配置文件有以下幾個應用場景。 * 使用 Spring Cloud Config 配置中心時,這時需要在 bootstrap 配置文件中添加連接到配置中心的配置屬性來加載外部配置中心的配置信息; * 一些固定的不能被覆蓋的屬性; * 一些加密/解密的場景; ## Spring Boot 的配置文件有哪幾種格式?它們有什么區別 .properties 和 .yml,它們的區別主要是書寫格式不同。 1).properties ``` app.user.name?=?javastack ``` 2).yml ``` app:?? user:???? name:?javastack ``` 另外,.yml 格式不支持?`@PropertySource`?注解導入配置。 ## Spring Boot 的核心注解是哪個?它主要由哪幾個注解組成的? 啟動類上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要組合包含了以下 3 個注解: @SpringBootConfiguration:組合了 @Configuration 注解,實現配置文件的功能。 @EnableAutoConfiguration:打開自動配置的功能,也可以關閉某個自動配置的選項,如關閉數據源自動配置功能:? ? ? ? ? ? ?@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。 @ComponentScan:Spring組件掃描。 ## 開啟 Spring Boot 特性有哪幾種方式 1)繼承spring-boot-starter-parent項目 2)導入spring-boot-dependencies項目依賴 ## Spring Boot 需要獨立的容器運行嗎 可以不需要,內置了 Tomcat/ Jetty 等容器 ## 運行 Spring Boot 有哪幾種方式 1)打包用命令或者放到容器中運行 2)用 Maven/ Gradle 插件運行 3)直接執行 main 方法運行 ## Spring Boot 自動配置原理是什么 注解 @EnableAutoConfiguration, @Configuration, @ConditionalOnClass 就是自動配置的核心,首先它得是一個配置文件,其次根據類路徑下是否有這個類去自動配置。 ## 如何理解 Spring Boot 中的 Starters Starters可以理解為啟動器,它包含了一系列可以集成到應用里面的依賴包,你可以一站式集成 Spring 及其他技術,而不需要到處找示例代碼和依賴包。如你想使用 Spring JPA 訪問數據庫,只要加入 spring-boot-starter-data-jpa 啟動器依賴就能使用了。 Starters包含了許多項目中需要用到的依賴,它們能快速持續的運行,都是一系列得到支持的管理傳遞性依賴。 ## 如何在 Spring Boot 啟動的時候運行一些特定的代碼 可以實現接口 ApplicationRunner 或者 CommandLineRunner,這兩個接口實現方式一樣,它們都只提供了一個 run 方法。 ## Spring Boot 有哪幾種讀取配置的方式 Spring Boot 可以通過 @PropertySource,@Value,@Environment, @ConfigurationProperties 來綁定變量 ## Spring Boot 支持哪些日志框架?推薦和默認的日志框架是哪個 Spring Boot 支持 Java Util Logging, Log4j2, Lockback 作為日志框架,如果你使用 Starters 啟動器,Spring Boot 將使用 **Logback **作為默認日志框架。 ## SpringBoot 實現熱部署有哪幾種方式 主要有兩種方式: * Spring Loaded * Spring-boot-devtools ## 如何理解 Spring Boot 配置加載順序 在 Spring Boot 里面,可以使用以下幾種方式來加載配置。 1)properties文件; 2)YAML文件; 3)系統環境變量; 4)命令行參數; 等等…… ## Spring Boot 如何定義多套不同環境配置 提供多套配置文件,如: applcation.properties application-dev.properties application-test.properties 利用**profile**參數指定環境。 ## Spring Boot 可以兼容老 Spring 項目嗎 可以兼容,使用?`@ImportResource`?注解導入老 Spring 項目配置文件。 ## 保護 Spring Boot 應用有哪些方法 * 在生產中使用HTTPS * 使用Snyk檢查你的依賴關系 * 升級到最新版本 * 啟用CSRF保護 * 使用內容安全策略防止XSS攻擊 ## 什么是Swagger Swagger廣泛用于可視化API,使用Swagger UI為前端開發人員提供在線沙箱。Swagger是用于生成RESTful Web服務的可視化表示的工具,規范和完整框架實現。它使文檔能夠以與服務器相同的速度更新。當通過Swagger正確定義時,消費者可以使用最少量的實現邏輯來理解遠程服務并與其進行交互。因此,Swagger消除了調用服務時的猜測。 ## 什么是JavaConfig Spring JavaConfig是Spring社區的產品,它提供了配置Spring IoC容器的純Java方法。因此它有助于避免使用XML配置。使用JavaConfig的優點在于: * 面向對象的配置。由于配置被定義為JavaConfig中的類,因此用戶可以充分利用Java中的面向對象功能。一個配置類可以繼承另一個,重寫它的@Bean方法等。 * 減少或消除XML配置。基于依賴注入原則的外化配置的好處已被證明。但是,許多開發人員不希望在XML和Java之間來回切換。JavaConfig為開發人員提供了一種純Java方法來配置與XML配置概念相似的Spring容器。從技術角度來講,只使用JavaConfig配置類來配置容器是可行的,但實際上很多人認為將JavaConfig與XML混合匹配是理想的。 * 類型安全和重構友好。JavaConfig提供了一種類型安全的方法來配置Spring容器。由于Java 5.0對泛型的支持,現在可以按類型而不是按名稱檢索bean,不需要任何強制轉換或基于字符串的查找。 # Spring Cloud ## spring Cloud和 Dubbo有哪些區別 本質區別:? * dubbo?是?基于 RPC 遠程?過程調用? * cloud?是基于?http??rest api 調用 | | dubbo | Spring Cloud | | --- | --- | --- | | 服務注冊中心 | Zookeeper | Spring Cloud Netflix Eureka | | 服務調用方式 | RPC | Restful API | | 監控服務 | Dubbo-monitor | Spring Boot Admin | | 斷路器 | 不完善 | Spring Cloud Netflix Hystrix | | 服務網關 | 無 | Spring Cloud Netflix Zuul | | 分布式配置 | 無 | Spring Cloud Config | | 服務跟蹤 | 無 | Spring Cloud Sleuth | | 消息總線 | 無 | Spring Cloud Bus | | 數據流 | 無 | Spring Cloud Stream | | 批量任務 | 無 | Spring Cloud Task | 很明顯, Spring Cloud的功能比 DUBBO更加強大,涵蓋面更廣,而且作為 Spring的挙頭項目,它也能夠與 Spring FrameworkSpring Boot.、 Spring Data、 Spring Batch等其他 Springi項目完美融合,這些對于微服務而言是至關重要的。使用 Dubbo構建的微服務架構就像組裝電腦,各環節我們的選擇自由度很高,但是最終結果很有可能因為一條內存質量不行就點不亮了,總是讓人不怎么放心,但是如果你是一名高手,那這些都不是問題;而 Spring Cloud就像品牌機,在 Spring Source的整合下,做了大量的兼容性測試,保證了機器擁有更高的穩定性,但是如果要在使用非原裝組件外的東西,就需要對其基礎有足夠的了解。? ## REST與RPC概念 **REST** REST是一種架構風格,指的是一組架構約束條件和原則。**滿足這些約束條件和原則的應用程序或設計就是 RESTful**。REST規范把所有內容都視為資源,網絡上一切皆資源。 REST并沒有創造新的技術,組件或服務,只是使用Web的現有特征和能力。 可以完全通過HTTP協議實現,**使用 HTTP 協議處理數據通信**。REST架構對資源的操作包括獲取、創建、修改和刪除資源的操作正好對應HTTP協議提供的GET、POST、PUT和DELETE方法。 **RPC** 遠程方法調用,就是像調用本地方法一樣調用遠程方法。RPC框架要做到的最基本的三件事: 1、服務端如何確定客戶端要調用的函數; 在遠程調用中,客戶端和服務端分別維護一個【ID->函數】的對應表, ID在所有進程中都是唯一確定的。客戶端在做遠程過程調用時,附上這個ID,服務端通過查表,來確定客戶端需要調用的函數,然后執行相應函數的代碼。 2、如何進行序列化和反序列化; 客戶端和服務端交互時將參數或結果轉化為字節流在網絡中傳輸,那么數據轉化為字節流的或者將字節流轉換成能讀取的固定格式時就需要進行序列化和反序列化,**序列化和反序列化**的速度也會影響遠程調用的效率。 3、如何進行網絡傳輸(選擇何種網絡協議); **多數RPC框架選擇TCP作為傳輸協議**,也有部分選擇HTTP。如gRPC使用HTTP2。不同的協議各有利弊。**TCP更加高效,而HTTP在實際應用中更加的靈活。** **REST與RPC應用場景** REST和RPC都常用于微服務架構中。 1、**HTTP相對更規范,更標準,更通用,無論哪種語言都支持http協議**。如果你是對外開放API,例如開放平臺,外部的編程語言多種多樣,你無法拒絕對每種語言的支持,現在開源中間件,基本最先支持的幾個協議都包含RESTful。 2、 RPC 框架作為架構微服務化的基礎組件,它能**大大降低架構微服務化的成本**,提高調用方與服務提供方的研發效率,屏蔽跨進程調用函數(服務)的各類復雜細節。讓調用方感覺就像調用本地函數一樣調用遠端函數、讓服務提供方感覺就像實現一個本地函數一樣來實現服務。 ## 你在項目開發中碰到的坑,你所知道的微服務技術棧有哪些 我們在討論分布式的微服務架構的時候? 它需要有哪些維度?? 1?服務治理? ?2?服務注冊 3?服務調用 4?負載均衡 5?服務監控 具體為: 1. 服務開發 :spring boot spring mvc? spring? 2. 服務的配置與管理 :?netfix?公司?archaius?阿里的diamond等 3. 服務的注冊與發現 :spriing cloud?所采用的?eureka,consul,zookeeper?等 4. 服務的調用:rest GRPC RPC? 5. .服務的熔斷器 :hystrix 、envoy等 6. 負載均衡 :ribbon 、nginx 7. 服務接口調用(客戶端調用服務的簡化工具) Feign等消息隊列Kafka、 Rabbitmq、 Activemq等 8. 服務配置中心管理Spring Cloud Config、Chef等 9. 服務路由(API網關)Zuul等 10. 服務監控Zabbix、 Nagios、 Metrics、 Spectator等 11. 全鏈路追蹤Slueth、Zipkin, Brave、 Dapper等 12. 服務部罟Docker、 Open Stack、 Kubernetes等 13. 數據流操作開發包Spring Cloud Stream(封裝與 Redis, Rabbit、 Kafka等發送接收消息) 14. 事件消息總線Spring Cloud Bus ## CAP 是什么 所謂的CAP? **C強一致性? A可用性 P 分區容錯性** 著名的CAP理論指出: ?一個分布式系統不可能同時滿足C(一致性)、A(可用性)和P(分區容錯性)。由于**分區容錯性P在是分布式系統中必須要保證的**,因此我們只能在A和C之間進行權衡。? **zookeeper 遵守 CP,eureka 遵守 AP** ## 什么是Spring Cloud? Spring Cloud是一個含概多個子項目的開發工具集,集合了眾多的開源框架,他利用了Spring Boot開發的便利性實現了很多功能,如服務注冊,服務注冊發現,負載均衡等.Spring Cloud在整合過程中主要是針對Netflix(耐非)開源組件的封裝。**SpringCloud是關注全局的微服務協調整理治理框架**,整合并管理各個微服務,為各個微服務之間提供,配置管理,服務發現,斷路器,路由,事件總線等集成服務。 ## 什么是服務熔斷?什么是服務降級 在復雜的分布式系統中,微服務之間的相互調用,有可能出現各種各樣的原因導致服務的阻塞,在高并發場景下,服務的阻塞意味著線程的阻塞,導致當前線程不可用,服務器的線程全部阻塞,導致服務器崩潰,由于服務之間的調用關系是同步的,會對整個微服務系統造成服務雪崩 為了解決某個微服務的調用響應時間過長或者不可用進而占用越來越多的系統資源引起雪崩效應就需要進行服務熔斷和服務降級處理。 所謂的服務熔斷指的是某個服務故障或異常一起類似顯示世界中的“保險絲"當某個異常條件被觸發就直接熔斷整個服務,而不是一直等到此服務超時。 服務熔斷就是相當于我們電閘的保險絲,一旦發生服務雪崩的,就會熔斷整個服務,通過維護一個自己的線程池,當線程達到閾值的時候就啟動服務降級,如果其他請求繼續訪問就直接返回fallback的默認值 。 ## 為什么需要負載均衡 為了提供并發量,有時同一個服務提供者可以部署多個。這個客戶端在調用時要根據一定的負載均衡策略完成負載調用。Feign是以接口方式進行調用,而不是通過RestTemplate來調用,**feign底層還是ribbon**,它進行了封裝,讓我們調用起來更加容易。 當對同一個服務部署多個時,就要涉及負載均衡調用了,這是可以選擇**Ribbon和Feign**。 ## 什么是ELK ELK堆棧由三個開源產品組成——Elasticsearch、Logstash和Kibana from Elastic。 Elasticsearch是一個基于Lucene搜索引擎的NoSQL數據庫。     Logstash是一個日志管道工具,它接受來自不同來源的輸入,執行不同的轉換,并將數據導出到不同的目標。它是一個動態的數據收集管道,具有可擴展的插件生態系統和強大的彈性搜索協同作用 Kibana是一個可視化UI層,工作在Elasticsearch之上。 這三個項目一起用于各種環境中的日志分析。因此Logstash收集和解析日志、彈性搜索索引并存儲這些信息,而Kibana提供了一個UI層,提供可操作的可見性。 ## 什么是WebSocket WebSocket是一種計算機通信協議,通過單個TCP連接提供全雙工通信通道。 WebSocket是雙向的——使用WebSocket客戶端或服務器都可以發起發送消息。 WebSocket是全雙工的——客戶端和服務器之間的通信是相互獨立的。 單個TCP連接——初始連接使用HTTP,然后將此連接升級為基于套接字的連接。然后,這個單一連接將用于未來的所有通信 輕- WebSocket消息數據交換比http輕得多。 # Redis ## 概念 簡單來說 redis 就是一個數據庫,不過與傳統數據庫不同的是 redis 的數據是存在內存中的,所以存寫速度非常快, 因此 redis 被廣泛應用于緩存方向。另外,redis 也經常用來做分布式鎖。redis 提供了多種數據類型來支持不同的業 務場景。除此之外,redis 支持事務 、持久化、LUA腳本、LRU驅動事件、多種集群方案。 ## 為什么要用 redis /為什么要用緩存 **高性能**: 假如用戶第一次訪問數據庫中的某些數據。這個過程會比較慢,因為是從硬盤上讀取的。將該用戶訪問的數據存在數 緩存中,這樣下一次再訪問這些數據的時候就可以直接從緩存中獲取了。操作緩存就是直接操作內存,所以速度相當 快。如果數據庫中的對應數據改變的之后,同步改變緩存中相應的數據即可! **高并發**: 直接操作緩存能夠承受的請求是遠遠大于直接訪問數據庫的,所以我們可以考慮把數據庫中的部分數據轉移到緩存中 去,這樣用戶的一部分請求會直接到緩存這里而不用經過數據庫。 ## 為什么要用 redis 而不用 map/guava 做緩存? 緩存分為**本地緩存和分布式緩存**。以 Java 為例,使用自帶的 map 或者 guava 實現的是本地緩存,最主要的特點是 輕量以及快速,**生命周期隨著 jvm 的銷毀而結束**,并且在多實例的情況下,每個實例都需要各自保存一份緩存,緩 存**不具有一致性**。 使用 redis 或 memcached 之類的稱為分布式緩存,在多實例的情況下,**各實例共用一份緩存數據**,緩存具有一致 性。缺點是需要保持 redis 或 memcached服務的高可用,整個程序架構上較為復雜。 ## redis 和 memcached 的區別 1. redis支持更豐富的數據類型(支持更復雜的應用場景):Redis不僅僅支持簡單的k/v類型的數據,同時還提供 list,set,zset,hash等數據結構的存儲。memcache支持簡單的數據類型,String。 2. Redis支持數據的持久化,可以將內存中的數據保持在磁盤中,重啟的時候可以再次加載進行使用,而 Memecache把數據全部存在內存之中。 3. 集群模式:memcached沒有原生的集群模式,需要依靠客戶端來實現往集群中分片寫入數據;但是 redis 目前 是原生支持 cluster 模式的. 4. Memcached是多線程,非阻塞IO復用的網絡模型;Redis使用單線程的多路 IO 復用模型。 ## redis 常見數據結構 String、Hash、List、Set、Sort Set(ZSet) ## redis 設置過期時間 **定期刪除**:redis默認是每隔 100ms 就隨機抽取一些設置了過期時間的key,檢查其是否過期,如果過期就刪 除。注意這里是隨機抽取的。為什么要隨機呢?你想一想假如 redis 存了幾十萬個 key ,每隔100ms就遍歷所 有的設置過期時間的 key 的話,就會給 CPU 帶來很大的負載! **惰性刪除** :定期刪除可能會導致很多過期 key 到了時間并沒有被刪除掉。所以就有了惰性刪除。假如你的過期 key,靠定期刪除沒有被刪除掉,還停留在內存里,除非你的系統去查一下那個 key,才會被redis給刪除掉。這 就是所謂的惰性刪除,也是夠懶的哈! ## 總結: **redis是單線程,線程安全** redis可以能夠快速執行的原因: 1. 絕大部分請求是純粹的內存操作(非常快速) 2. 采用單線程,避免了不必要的上下文切換和競爭條件 3. 非阻塞IO - IO多路復用 IO多路復用中有三種方式:select,poll,epoll。需要注意的是,select,poll是線程不安全的,epoll是線程安全的 **redis內部實現采用epoll**,采用了epoll+自己實現的簡單的事件框架。epoll中的讀、寫、關閉、連接都轉化成了事件,然后利用epoll的多路復用特性,絕不在io上浪費一點時間 這3個條件不是相互獨立的,特別是第一條,如果請求都是耗時的,采用單線程吞吐量及性能可想而知了。應該說redis為特殊的場景選擇了合適的技術方案。 # Spring ## 什么是 Spring 框架 Spring 是一種輕量級開發框架,旨在提高開發人員的開發效率以及系統的可維護性。 我們一般說 Spring 框架指的都是 Spring Framework,它是很多模塊的集合,使用這些模塊可以很方便地協助我們進行開發。這些模塊是:核心容器、數據訪問/集成,、Web、AOP(面向切面編程)、工具、消息和測試模塊。比如:Core Container 中的 Core 組件是Spring 所有組件的核心,Beans 組件和 Context 組件是實現IOC和依賴注入的基礎,AOP組件用來實現面向切面編程。 Spring 官網列出的 Spring 的 6 個特征: * **核心技術**?:依賴注入(DI),AOP,事件(events),資源,i18n,驗證,數據綁定,類型轉換,SpEL。 * **測試**?:模擬對象,TestContext框架,Spring MVC 測試,WebTestClient。 * **數據訪問**?:事務,DAO支持,JDBC,ORM,編組XML。 * **Web支持**?: Spring MVC和Spring WebFlux Web框架。 * **集成**?:遠程處理,JMS,JCA,JMX,電子郵件,任務,調度,緩存。 * **語言**?:Kotlin,Groovy,動態語言。 ## IoC IoC(Inverse of Control:控制反轉)是一種**設計思想**,就是?**將原本在程序中手動創建對象的控制權,交由Spring框架來管理。**?IoC 在其他語言中也有應用,并非 Spirng 特有。?**IoC 容器是 Spring 用來實現 IoC 的載體, IoC 容器實際上就是個Map(key,value),Map 中存放的是各種對象。** 將對象之間的相互依賴關系交給 IOC 容器來管理,并由 IOC 容器完成對象的注入。這樣可以很大程度上簡化應用的開發,把應用從復雜的依賴關系中解放出來。?**IOC 容器就像是一個工廠一樣,當我們需要創建一個對象的時候,只需要配置好配置文件/注解即可,完全不用考慮對象是如何被創建出來的。**?在實際項目中一個 Service 類可能有幾百甚至上千個類作為它的底層,假如我們需要實例化這個 Service,你可能要每次都要搞清這個 Service 所有底層類的構造函數,這可能會把人逼瘋。如果利用 IOC 的話,你只需要配置好,然后在需要的地方引用就行了,這大大增加了項目的可維護性且降低了開發難度。 Spring 時代我們一般通過 XML 文件來配置 Bean,后來開發人員覺得 XML 文件來配置不太好,于是 SpringBoot 注解配置就慢慢開始流行起來。 **Spring IOC的初始化過程:** ![](https://box.kancloud.cn/4a43459dc172af88e817875e205ea4b0_709x56.png) ## AOP AOP(Aspect-Oriented Programming:面向切面編程)能夠將那些與業務無關,**卻為業務模塊所共同調用的邏輯或責任(例如事務處理、日志管理、權限控制等)封裝起來**,便于**減少系統的重復代碼**,**降低模塊間的耦合度**,并**有利于未來的可拓展性和可維護性**。 **Spring AOP就是基于動態代理的**,如果要代理的對象,實現了某個接口,那么Spring AOP會使用**JDK Proxy**,去創建代理對象,而對于沒有實現接口的對象,就無法使用 JDK Proxy 去進行代理了,這時候Spring AOP會使用**Cglib**?,這時候Spring AOP會使用?**Cglib**?生成一個被代理對象的子類來作為代理,如下圖所示: ![](https://box.kancloud.cn/b55df1d8a80199f0167157ef84f8f15c_720x354.png) 使用 AOP 之后我們可以把一些通用功能抽象出來,在需要用到的地方直接使用即可,這樣大大簡化了代碼量。我們需要增加新功能時也方便,這樣也提高了系統擴展性。日志功能、事務管理等等場景都用到了 AOP 。 ## Spring AOP 和 AspectJ AOP 有什么區別? **Spring AOP 屬于運行時增強,而 AspectJ 是編譯時增強。**?Spring AOP 基于代理(Proxying),而 AspectJ 基于字節碼操作(Bytecode Manipulation)。 Spring AOP 已經集成了 AspectJ ,AspectJ 應該算的上是 Java 生態系統中最完整的 AOP 框架了。AspectJ 相比于 Spring AOP 功能更加強大,但是 Spring AOP 相對來說更簡單, 如果我們的切面比較少,那么兩者性能差異不大。但是,當切面太多的話,最好選擇 AspectJ ,它比Spring AOP 快很多。 ## Spring 中的 bean 的作用域有哪些? * singleton : 唯一 bean 實例,Spring 中的 bean 默認都是單例的。 * prototype : 每次請求都會創建一個新的 bean 實例。 * request : 每一次HTTP請求都會產生一個新的bean,該bean僅在當前HTTP request內有效。 * session : 每一次HTTP請求都會產生一個新的 bean,該bean僅在當前 HTTP session 內有效。 * global-session: 全局session作用域,僅僅在基于portlet的web應用中才有意義,Spring5已經沒有了。Portlet是能夠生成語義代碼(例如:HTML)片段的小型Java Web插件。它們基于portlet容器,可以像servlet一樣處理HTTP請求。但是,與 servlet 不同,每個 portlet 都有不同的會話 ## Spring 中的單例 bean 的線程安全問題了解嗎? 大部分時候我們并沒有在系統中使用多線程,所以很少有人會關注這個問題。單例 bean 存在線程問題,主要是因為當多個線程操作同一個對象的時候,對這個對象的非靜態成員變量的寫操作會存在線程安全問題。 常見的有兩種解決辦法: 1. 在Bean對象中盡量避免定義可變的成員變量(不太現實)。 2. 在類中定義一個ThreadLocal成員變量,將需要的可變成員變量保存在 ThreadLocal 中(推薦的一種方式)。 ## Spring 中的 bean 生命周期 * Bean 容器找到配置文件中 Spring Bean 的定義。 * Bean 容器利用 Java Reflection API 創建一個Bean的實例。 * 如果涉及到一些屬性值 利用?`set()`方法設置一些屬性值。 * 如果 Bean 實現了?`BeanNameAware`?接口,調用?`setBeanName()`方法,傳入Bean的名字。 * 如果 Bean 實現了?`BeanClassLoaderAware`?接口,調用?`setBeanClassLoader()`方法,傳入?`ClassLoader`對象的實例。 * 如果Bean實現了?`BeanFactoryAware`?接口,調用?`setBeanClassLoader()`方法,傳入?`ClassLoade`?r對象的實例。 * 與上面的類似,如果實現了其他?`*.Aware`接口,就調用相應的方法。 * 如果有和加載這個 Bean 的 Spring 容器相關的?`BeanPostProcessor`?對象,執行`postProcessBeforeInitialization()`?方法 * 如果Bean實現了`InitializingBean`接口,執行`afterPropertiesSet()`方法。 * 如果 Bean 在配置文件中的定義包含 init-method 屬性,執行指定的方法。 * 如果有和加載這個 Bean的 Spring 容器相關的?`BeanPostProcessor`?對象,執行`postProcessAfterInitialization()`?方法 * 當要銷毀 Bean 的時候,如果 Bean 實現了?`DisposableBean`?接口,執行?`destroy()`?方法。 * 當要銷毀 Bean 的時候,如果 Bean 在配置文件中的定義包含 destroy-method 屬性,執行指定的方法。 ![](https://box.kancloud.cn/304a2750ed19255e9edc54b281c33631_720x303.png) ## 說說自己對于 Spring MVC 了解? * **Model1 時代**?: 很多學 Java 后端比較晚的朋友可能并沒有接觸過 Model1 模式下的 JavaWeb 應用開發。在 Model1 模式下,整個 Web 應用幾乎全部用 JSP 頁面組成,只用少量的 JavaBean 來處理數據庫連接、訪問等操作。這個模式下 JSP 即是控制層又是表現層。顯而易見,這種模式存在很多問題。比如①將控制邏輯和表現邏輯混雜在一起,導致代碼重用率極低;②前端和后端相互依賴,難以進行測試并且開發效率極低; * **Model2 時代**?:學過 Servlet 并做過相關 Demo 的朋友應該了解“Java Bean(Model)+ JSP(View,)+Servlet(Controller) ”這種開發模式,這就是早期的 JavaWeb MVC 開發模式。Model:系統涉及的數據,也就是 dao 和 bean。View:展示模型中的數據,只是用來展示。Controller:處理用戶請求都發送給 ,返回數據給 JSP 并展示給用戶。 Model2 模式下還存在很多問題,Model2的抽象和封裝程度還遠遠不夠,使用Model2進行開發時不可避免地會重復造輪子,這就大大降低了程序的可維護性和復用性。于是很多JavaWeb開發相關的 MVC 框架營運而生比如Struts2,但是 Struts2 比較笨重。隨著 Spring 輕量級開發框架的流行,Spring 生態圈出現了 Spring MVC 框架, Spring MVC 是當前最優秀的 MVC 框架。相比于 Struts2 , Spring MVC 使用更加簡單和方便,開發效率更高,并且 Spring MVC 運行速度更快。 MVC 是一種設計模式,Spring MVC 是一款很優秀的 MVC 框架。Spring MVC 可以幫助我們進行更簡潔的Web層的開發,并且它天生與 Spring 框架集成。Spring MVC 下我們一般把后端項目分為 Service層(處理業務)、Dao層(數據庫操作)、Entity層(實體類)、Controller層(控制層,返回數據給前臺頁面)。 **Spring MVC 的簡單原理圖如下:** ![](https://box.kancloud.cn/f1632c8003765a277da3039471d35a42_804x369.png) ## SpringMVC 工作原理了解嗎? **原理如下圖所示:** ![](https://box.kancloud.cn/07c38e076a21ad76abe0bb105b467957_1015x466.png) 上圖的一個筆誤的小問題:Spring MVC 的入口函數也就是前端控制器?`DispatcherServlet`?的作用是接收請求,響應結果。 **流程說明(重要):** 1. 客戶端(瀏覽器)發送請求,直接請求到?`DispatcherServlet`。 2. `DispatcherServlet`?根據請求信息調用?`HandlerMapping`,解析請求對應的?`Handler`。 3. 解析到對應的?`Handler`(也就是我們平常說的?`Controller`?控制器)后,開始由?`HandlerAdapter`?適配器處理。 4. `HandlerAdapter`?會根據?`Handler`來調用真正的處理器開處理請求,并處理相應的業務邏輯。 5. 處理器處理完業務后,會返回一個?`ModelAndView`?對象,`Model`?是返回的數據對象,`View`?是個邏輯上的?`View`。 6. `ViewResolver`?會根據邏輯?`View`?查找實際的?`View`。 7. `DispaterServlet`?把返回的?`Model`?傳給?`View`(視圖渲染)。 8. 把?`View`?返回給請求者(瀏覽器) ## Spring 框架中用到了哪些設計模式 * **工廠設計模式**?: Spring使用工廠模式通過?`BeanFactory`、`ApplicationContext`?創建 bean 對象。 * **代理設計模式**?: Spring AOP 功能的實現。 * **單例設計模式**?: Spring 中的 Bean 默認都是單例的。 * **模板方法模式**?: Spring 中?`jdbcTemplate`、`hibernateTemplate`?等以 Template 結尾的對數據庫操作的類,它們就使用到了模板模式。 * **包裝器設計模式**?: 我們的項目需要連接多個數據庫,而且不同的客戶在每次訪問中根據需要會去訪問不同的數據庫。這種模式讓我們可以根據客戶的需求能夠動態切換不同的數據源。 * **觀察者模式:**?Spring 事件驅動模型就是觀察者模式很經典的一個應用。 * **適配器模式**?:Spring AOP 的增強或通知(Advice)使用到了適配器模式、spring MVC 中也是用到了適配器模式適配`Controller`。 ## @Component 和 @Bean 的區別是什么 1. 作用對象不同:?`@Component`?注解作用于類,而`@Bean`注解作用于方法。 2. `@Component`通常是通過類路徑掃描來自動偵測以及自動裝配到Spring容器中(我們可以使用?`@ComponentScan`?注解定義要掃描的路徑從中找出標識了需要裝配的類自動裝配到 Spring 的 bean 容器中)。`@Bean`?注解通常是我們在標有該注解的方法中定義產生這個 bean,`@Bean`告訴了Spring這是某個類的實例,當我需要用它的時候還給我。 3. `@Bean`?注解比?`Component`?注解的自定義性更強,而且很多地方我們只能通過?`@Bean`?注解來注冊bean。比如當我們引用第三方庫中的類需要裝配到?`Spring`容器時,則只能通過?`@Bean`來實現。 下面這個例子是通過?`@Component`?無法實現的。 ``` @Bean public?OneService?getService(status)?{ case?(status)??{ ????????when?1: returnnew?serviceImpl1(); ????????when?2: returnnew?serviceImpl2(); ????????when?3: returnnew?serviceImpl3(); ????} } ``` ## 將一個類聲明為Spring的 bean 的注解有哪些 我們一般使用?`@Autowired`?注解自動裝配 bean,要想把類標識成可用于?`@Autowired`注解自動裝配的 bean 的類,采用以下注解可實現: * `@Component`?:通用的注解,可標注任意類為?`Spring`?組件。如果一個Bean不知道屬于哪個層,可以使用`@Component`?注解標注。 * `@Repository`?: 對應持久層即 Dao 層,主要用于數據庫相關操作。 * `@Service`?: 對應服務層,主要涉及一些復雜的邏輯,需要用到 Dao層。 * `@Controller`?: 對應 Spring MVC 控制層,主要用戶接受用戶請求并調用 Service 層返回數據給前端頁面。 ## Spring 管理事務的方式有幾種 1. 編程式事務,在代碼中硬編碼。(不推薦使用) 2. 聲明式事務,在配置文件中配置(推薦使用) **聲明式事務又分為兩種:** 1. 基于XML的聲明式事務 2. 基于注解的聲明式事務 # 數據結構之數組、鏈表、棧、隊列 ## 一 數組 **數組(Array)**?是一種很常見的數據結構。它是由相同類型的元素(element)的集合所組成,并且被分配一塊連續的內存來存儲(與鏈表對比)。利用元素的索引(index)可以計算出該元素對應的存儲地址。它的特點是提供隨機訪問并且容量有限。 ``` 假如數組的長度為?n。 訪問:O(1)//訪問特定位置的元素??? 插入:O(n?)//最壞的情況發生在插入發生在數組的首部并需要移動所有元素時 刪除:O(n)//最壞的情況發生在刪除數組的開頭發生并需要移動第一元素后面所有的元素時 ``` ![](https://box.kancloud.cn/9a33606bb8fb13a645c34427b220b03e_380x177.png) ## 二 鏈表 **鏈表(LinkedList)**?雖然是一種線性表,但是并不會按線性的順序存儲數據,而是在每一個節點里存到下一個節點的指針(Pointer)。由于不必須按順序存儲,鏈表在插入和刪除的時候可以達到 O(1) 的復雜度,比另一種線性表順序表快得多,但是查找一個節點或者訪問特定編號的節點則需要 O(n) 的時間,而順序表相應的時間復雜度分別是O(logn) 和O(1)。 **使用鏈表結構可以克服數組需要預先知道數據大小的缺點,鏈表結構可以充分利用計算機內存空間,實現靈活的內存動態管理。但鏈表不會節省空間,相比于數組會占用更多的空間,因為鏈表中每個節點存放的還有指向其他節點的指針。鏈表不具有數組隨機讀取的優點,但是插入刪除元素的時間復雜度為O(1)** **鏈表分類** **常見鏈表分類:** 1. 單鏈表 2. 雙向鏈表 3. 循環鏈表 4. 雙向循環鏈表 ``` 假如鏈表中有n個元素。 訪問:O(n)//訪問特定位置的元素 插入刪除:O(1)//必須要要知道插入元素的位置 ``` #### 2.2.1 單鏈表 **單鏈表**?單向鏈表只有一個方向,結點只有一個后繼指針 next 指向后面的節點。因此,鏈表這種數據結構通常在物理內存上是不連續的。我們習慣性地把第一個結點叫作頭結點,鏈表通常有一個不保存任何值的 head 節點(頭結點),通過頭結點我們可以遍歷整個鏈表。尾結點通常指向null。 ![](https://box.kancloud.cn/a7175e4341e48442fd054720fc74923d_591x181.png) #### 2.2.2 循環鏈表 **循環鏈表**?其實是一種特殊的單鏈表,和單鏈表不同的是循環鏈表的尾結點不是指向null,而是指向鏈表的頭結點。 ![](https://box.kancloud.cn/8ac54ac29e3690b8abbd73b8f17b6380_552x171.png) #### 2.2.3 雙向鏈表 **雙向鏈表**?包含兩個指針,一個prev指向前一個節點,一個next指向后一個節點。 ![](https://box.kancloud.cn/724f97d98bb7a037f1ee703f501437cb_488x215.png) #### 2.2.4 雙向循環鏈表 **雙向循環鏈表**?最后一個節點的 next 指向head,而 head 的prev指向最后一個節點,構成一個環。 ![](https://box.kancloud.cn/5ec2f7f6192085f1f350d10b97f72ec9_530x247.png) ### 2.3 數組vs鏈表 1. 數組使用的是連續內存空間對CPU的緩存機制友好,鏈表則相反。 2. 數組的大小固定,聲明之后就要占用所需的連續內存空間。如果聲明的數組過小的話,需要再申請一個更大的內存空間,然后將原數組拷貝進去。數組多的情況下,這將是非常耗時的。鏈表則天然支持動態擴容。 ## 三 棧 ### 3.1 棧簡介 **棧**?(stack)只允許在有序的線性數據集合的一端(稱為棧頂 top)進行加入數據(push)和移除數據(pop)。因而按照?**后進先出(LIFO, Last In First Out)**?的原理運作。**在棧中,push 和 pop 的操作都發生在棧頂。**?棧常用一維數組或鏈表來實現,用數組實現的隊列叫作?**順序棧**?,用鏈表實現的隊列叫作?**鏈式棧**?。 ``` 假設堆棧中有n個元素。 訪問:O(n)//最壞情況? 插入刪除:O(1)//頂端插入和刪除元素 ``` ![](https://box.kancloud.cn/76e54457b01cdf2983abd5524efcc125_497x257.png) ### 3.2 棧的常見應用常見應用場景 #### 3.2.1 實現瀏覽器的回退和前進功能 我們只需要使用兩個棧(Stack1和Stack2)和就能實現這個功能。比如你按順序查看了 1,2,3,4 這四個頁面,我們依次把 1,2,3,4 這四個頁面壓入 Stack1 中。當你想回頭看2這個頁面的時候,你點擊回退按鈕,我們依次把4,3這兩個頁面從Stack1 彈出,然后壓入 Stack2 中。假如你又想回到頁面3,你點擊前進按鈕,我們將3頁面從Stack2 彈出,然后壓入到 Stack1 中。示例圖如下: ![](https://box.kancloud.cn/3d37e435b7eb8c9034c9cf9c06addda1_682x483.png) #### 3.2.2 檢查符號是否成對出現 ``` 給定一個只包括?`'('`,`')'`,`'{'`,`'}'`,`'['`,`']'`?的字符串,判斷該字符串是否有效。 有效字符串需滿足: 1. 左括號必須用相同類型的右括號閉合。 2. 左括號必須以正確的順序閉合。 比如 "()"、"()\[\]{}"、"{\[\]}" 都是有效字符串,而 "(\]" 、"(\[)\]" 則不是。 ``` 這個問題實際是Leetcode的一道題目,我們可以利用棧?`Stack`?來解決這個問題。 1. 首先我們將括號間的對應規則存放在?`Map`?中,這一點應該毋容置疑; 2. 創建一個棧。遍歷字符串,如果字符是左括號就直接加入`stack`中,否則將`stack`的棧頂元素與這個括號做比較,如果不相等就直接返回false。遍歷結束,如果`stack`為空,返回?`true`。 ``` public?boolean?isValid(String?s){ //?括號之間的對應規則 ????HashMap?mappings?=?new?HashMap(); ????mappings.put(')',?'('); ????mappings.put('}',?'{'); ????mappings.put('\]',?'\['); ????Stack?stack?=?new?Stack(); char\[\]?chars?=?s.toCharArray(); for?(int?i?=?0;?i?<?chars.length;?i++)?{ if?(mappings.containsKey(chars\[i\]))?{ char?topElement?=?stack.empty()???'#'?:?stack.pop(); if?(topElement?!=?mappings.get(chars\[i\]))?{ returnfalse; ????????????} ????????}?else?{ ????????????stack.push(chars\[i\]); ????????} ????} return?stack.isEmpty(); } ``` #### 3.2.3 反轉字符串 將字符串中的每個字符先入棧再出棧就可以了。 #### 3.2.4 維護函數調用 最后一個被調用的函數必須先完成執行,符合棧的?**后進先出(LIFO, Last In First Out)**特性。 ## 四 隊列 ### 4.1 隊列簡介 **隊列**?是?**先進先出( FIFO,First In, First Out)**?的線性表。在具體應用中通常用鏈表或者數組來實現,用數組實現的隊列叫作?**順序隊列**?,用鏈表實現的隊列叫作?**鏈式隊列**?。**隊列只允許在后端(rear)進行插入操作也就是 入隊 enqueue,在前端(front)進行刪除操作也就是出隊 dequeue** 隊列的操作方式和堆棧類似,唯一的區別在于隊列只允許新數據在后端進行添加。 ``` 假設隊列中有n個元素。 訪問:O(n)//最壞情況 插入刪除:O(1)//后端插入前端刪除元素 ``` ![](https://box.kancloud.cn/547a0e041851a7129c9711c103a43b20_571x118.png) ### 4.2 隊列分類 #### 4.2.1 單隊列 單隊列就是常見的隊列, 每次添加元素時,都是添加到隊尾。單隊列又分為?**順序隊列(數組實現)**?和?**鏈式隊列(鏈表實現)**。 **順序隊列存在“假溢出”的問題也就是明明有位置卻不能添加的情況。** 假設下圖是一個順序隊列,我們將前兩個元素1,2 出隊,并入隊兩個元素7,8。當進行入隊、出隊操作的時候,front和 rear 都會持續往后移動,當 rear 移動到最后的時候,我們無法再往隊列中添加數據,即使數組中還有空余空間,這種現象就是?**”假溢出“**?。除了假溢出問題之外,如下圖所示,當添加元素8的時候,rear 指針移動到數組之外(越界)。 > 為了避免當只有一個元素的時候,隊頭和隊尾重合使處理變得麻煩,所以引入兩個指針,front 指針指向對頭元素,rear 指針指向隊列最后一個元素的下一個位置,這樣當 front 等于 rear 時,此隊列不是還剩一個元素,而是空隊列。——From 《大話數據結構》 ![](https://box.kancloud.cn/d262d16f85759091c9aad00e8a07aea0_540x232.png) #### 4.2.2 循環隊列 循環隊列可以解決順序隊列的假溢出和越界問題。解決辦法就是:從頭開始,這樣也就會形成頭尾相接的循環,這也就是循環隊列名字的由來。 還是用上面的圖,我們將 rear 指針指向數組下標為 0 的位置就不會有越界問題了。當我們再向隊列中添加元素的時候, rear 向后移動。 ![](https://box.kancloud.cn/211e3f3808ed980e32920f91561ea91e_478x451.png) 順序隊列中,我們說?`front==rear`?的時候隊列為空,循環隊列中則不一樣,也可能為滿,如上圖所示。解決辦法有兩種: 1. 可以設置一個標志變量?`flag`,當?`front==rear`?并且?`flag=0`?的時候隊列為空,當`front==rear`?并且?`flag=1`?的時候隊列為滿。 2. 隊列為空的時候就是?`front==rear`?,隊列滿的時候,我們保證數組還有一個空閑的位置,rear 就指向這個空閑位置,如下圖所示,那么現在判斷隊列是否為滿的條件就是:?`(rear+1) % QueueSize= front`?。 ![](https://box.kancloud.cn/e6fded6b8248540399848761139fae4a_484x112.png) ### 常見應用場景 * **阻塞隊列:**?阻塞隊列可以看成在隊列基礎上加了阻塞操作的隊列。當隊列為空的時候,出隊操作阻塞,當隊列滿的時候,入隊操作阻塞。使用阻塞隊列我們可以很容易實現“生產者 - 消費者“模型。 * **線程池中的請求/任務隊列:**?線程池中沒有空閑線程時,新的任務請求線程資源時,線程池該如何處理呢?答案是將這些請求放在隊列中,當有空閑線程的時候,會循環中反復從隊列中獲取任務來執行。隊列分為無界隊列(基于鏈表)和有界隊列(基于數組)。無界隊列的特點就是可以一直入列,除非系統資源耗盡,比如 :`FixedThreadPool`?使用無界隊列?`LinkedBlockingQueue`。但是有界隊列就不一樣了,當隊列滿的話后面再有任務/請求就會拒絕,在 Java 中的體現就是會拋出`java.util.concurrent.RejectedExecutionException`?異常。 * linux內核進程隊列(按優先級排隊) * 實現生活中的派對,播放器上的播放列表; * 消息隊列 * 等等…… ## Java反射中Method類invoke方法的用法 Method getMethod(String name, Class... parameterTypes)?? \--返回一個 Method 對象,它反映此 Class 對象所表示的類或接口的指定公共成員方法。?? 方法后面接收的就是Class類的對象,而如:String.class、int.class這些字節碼才是Class類的對象 也可以此種方式: ``` //getMethod第一個參數是方法名,第二個參數是該方法的參數類型, //因為存在同方法名不同參數這種情況,所以只有同時指定方法名和參數類型才能唯一確定一個方法 Method?method?=?XXX.getClass().getMethod(methodName,new?Class\[0\]); //第一個參數是具體調用該方法的對象 ?//第二個參數是執行該方法的具體參數 如一個函數 int Test(int a, String str); 對應的getMethod方法: 1.? getMethod("Test",int.class,String.class); 2. getMethod("Test",new Class\[\]{int.class,String.class}); ``` 然后通過invoke來調用此方法: 函數原型:Object.Java.lang.reflect.Method.invoke(Object receiver, Object... args) ``` //Method類的invoke(Object obj,Object args\[\])方法接收的參數必須為對象, ?//如果參數為基本類型數據,必須轉換為相應的包裝類型的對象。invoke()方法的返回值總是對象, ? //如果實際被調用的方法的返回類型是基本類型數據,那么invoke()方法會把它轉換為相應的包裝類型的對象,再將其返回 receiver:該方法所在類的一個對象 args: 傳入的參數 如 100,“hello” ``` ## ThreadLocal ### ThreadLocal是什么 ThreadLocal是一個**本地線程副本變量工具類**。主要用于將**私有線程和該線程存放的副本對象**做一個映射,各個線程之間的變量互不干擾,在高并發場景下,可以實現無狀態的調用,特別適用于各個線程依賴不同的變量值完成操作的場景。 **ThreadLocal的內部結構圖** ![](https://box.kancloud.cn/de8892a4be9fdd5609eb0a0c2b8032cb_692x744.png) 從上面的結構圖,我們已經窺見ThreadLocal的核心機制: * 每個Thread線程內部都有一個Map。 * Map里面存儲線程本地對象(key)和線程的變量副本(value) * 但是,Thread內部的Map是由ThreadLocal維護的,由ThreadLocal負責向map獲取和設置線程的變量值。 所以對于不同的線程,每次獲取副本值時,別的線程并不能獲取到當前線程的副本值,形成了副本的隔離,互不干擾。 ``` Thread線程內部的Map在類中描述如下: public class Thread implements Runnable { /* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null; } ``` ### 深入解析ThreadLocal ThreadLocal類提供如下幾個核心方法: ~~~ public T get() public void set(T value) public void remove() ~~~ * get()方法用于獲取當前線程的副本變量值。 * set()方法用于保存當前線程的副本變量值。 * initialValue()為當前線程初始副本變量值。 * remove()方法移除當前前程的副本變量值。 ### 應用場景 Hibernate的session獲取場景 ``` ~~~ private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>(); //獲取Session public static Session getCurrentSession(){ Session session = threadLocal.get(); //判斷Session是否為空,如果為空,將創建一個session,并設置到本地線程變量中 try { if(session ==null&&!session.isOpen()){ if(sessionFactory==null){ rbuildSessionFactory();// 創建Hibernate的SessionFactory }else{ session = sessionFactory.openSession(); } } threadLocal.set(session); } catch (Exception e) { // TODO: handle exception } return session; } ~~~ ``` 為什么?每個線程訪問數據庫都應當是一個獨立的Session會話,如果多個線程共享同一個Session會話,有可能其他線程關閉連接了,當前線程再執行提交時就會出現會話已關閉的異常,導致系統異常。此方式能避免線程爭搶Session,提高并發下的安全性。 使用ThreadLocal的典型場景正如上面的**數據庫連接管理**,**線程會話管理**等場景,只適用于獨立變量副本的情況,如果變量為全局共享的,則不適用在高并發下使用。 ### 原理 線程共享變量緩存如下: **Thread**.ThreadLocalMap; 1、**Thread: 當前線程,可以通過Thread.currentThread()獲取。** 2、**ThreadLocal:我們的static**ThreadLocal**變量。** 3、**Object:?當前線程共享變量。** 我們調用ThreadLocal.get方法時,實際上是從當前線程中獲取ThreadLocalMap,然后根據當前ThreadLocal獲取當前線程共享變量**Object。** ThreadLocal.set,ThreadLocal.remove實際上是同樣的道理。 這種存儲結構的好處: 1、線程死去的時候,線程共享變量ThreadLocalMap則銷毀。 2、ThreadLocalMap鍵值對數量為ThreadLocal的數量,一般來說ThreadLocal數量很少,相比在ThreadLocal中用Map鍵值對存儲線程共享變量(Thread數量一般來說比ThreadLocal數量多),性能提高很多。 關于**ThreadLocalMap弱引用問題**: 當線程沒有結束,但是ThreadLocal已經被回收,則可能導致線程中存在ThreadLocalMap的鍵值對,造成內存泄露。(ThreadLocal被回收,ThreadLocal關聯的線程共享變量還存在)。 雖然ThreadLocal的get,set方法可以清除ThreadLocalMap中key為null的value,但是get,set方法在內存泄露后并不會必然調用,所以為了防止此類情況的出現,我們有兩種手段。 1、使用完線程共享變量后,顯示調用ThreadLocalMap.remove方法清除線程共享變量; 2、JDK建議**ThreadLocal定義為private static**,這樣ThreadLocal的弱引用問題則不存在了。 ## 簡單介紹一下 Linux 文件系統 在Linux操作系統中,所有被操作系統管理的資源,例如網絡接口卡、磁盤驅動器、打印機、輸入輸出設備、普通文 件或是目錄都被看作是一個文件。 也就是說在LINUX系統中有一個重要的概念:**一切都是文件**。其實這是UNIX哲學的一個體現,而Linux是重寫UNIX而 來,所以這個概念也就傳承了下來。在UNIX系統中,把一切資源都看作是文件,包括硬件設備。UNIX系統把每個硬 件都看成是一個文件,通常稱為設備文件,這樣用戶就可以用讀寫文件的方式實現對硬件的訪問。 Linux支持5種文件類型 : ![](https://box.kancloud.cn/a8db4a9c71c3cb69e5b51655e7278ca3_1117x679.png) ## 一些常見的 Linux 命令 **目錄的操作命令(增刪改查) ** 1. mkdir 目錄名稱: 增加目錄 2. ls或者ll (ll是ls -l的縮寫,ll命令以看到該目錄下的所有目錄和文件的詳細信息):查看目錄信息 3. find 目錄 參數: 尋找目錄(查) 4. mv 目錄名稱 新目錄名稱: 修改目錄的名稱(改) 注意:mv的語法不僅可以對目錄進行重命名而且也可以對各種文件,壓縮包等進行 重命名的操作。mv命令用 來對文件或目錄重新命名,或者將文件從一個目錄移到另一個目錄中。后面會介紹到mv命令的另一個用法。 5. mv 目錄名稱 目錄的新位置: 移動目錄的位置---剪切(改) 注意:mv語法不僅可以對目錄進行剪切操作,對文件和壓縮包等都可執行剪切操作。另外mv與cp的結果不 同,mv好像文件“搬家”,文件個數并未增加。而cp對文件進行復制,文件個數增加了。 6. cp -r 目錄名稱 目錄拷貝的目標位置: 拷貝目錄(改),-r代表遞歸拷貝 注意:cp命令不僅可以拷貝目錄還可以拷貝文件,壓縮包等,拷貝文件和壓縮包時不 用寫-r遞歸 7. rm [-rf] 目錄: 刪除目錄(刪) 注意:rm不僅可以刪除目錄,也可以刪除其他文件或壓縮包,為了增強大家的記憶, 無論刪除任何目錄或文 件,都直接使用rm -rf 目錄/文件/壓縮包 **文件的操作命令(增刪改查) ** 1. touch 文件名稱: 文件的創建(增) 2. cat/more/less/tail 文件名稱 文件的查看(查) cat : 只能顯示最后一屏內容 more : 可以顯示百分比,回車可以向下一行, 空格可以向下一頁,q可以退出查看 less : 可以使用鍵盤上的PgUp和PgDn向上 和向下翻頁,q結束查看 tail-10 : 查看文件的后10行,Ctrl+C結束 注意:命令 tail -f 文件 可以對某個文件進行動態監控,例如tomcat的日志文件, 會隨著程序的運行,日志會變 化,可以使用tail -f catalina-2016-11-11.log 監控 文 件的變化 3. vim 文件: 修改文件的內容(改) vim編輯器是Linux中的強大組件,是vi編輯器的加強版,vim編輯器的命令和快捷方式有很多,但此處不一一闡 述,大家也無需研究的很透徹,使用vim編輯修改文件的方式基本會使用就可以了。 在實際開發中,使用vim編輯器主要作用就是修改配置文件,下面是一般步驟: vim 文件------>進入文件----->命令模式------>按i進入編輯模式----->編輯文件 ------->按Esc進入底行模式----->輸 入:wq/q! (輸入wq代表寫入內容并退出,即保存;輸入q!代表強制退出不保存。) 4. rm -rf 文件: 刪除文件(刪) 同目錄刪除:熟記 rm -rf 文件 即可 **其他常用命令 ** pwd : 顯示當前所在位置 grep 要搜索的字符串 要搜索的文件 --color : 搜索命令,--color代表高亮顯示 ps -ef / ps aux : 這兩個命令都是查看當前系統正在運行進程,兩者的區別是展示格式不同。如果想要查看 特定的進程可以使用這樣的格式: ps aux|grep redis (查看包括redis字符串的進程) 注意:如果直接用ps((Process Status))命令,會顯示所有進程的狀態,通常結合grep命令查看某進程的 狀態。 kill -9 進程的pid : 殺死進程(-9 表示強制終止。) 先用ps查找進程,然后用kill殺掉 **網絡通信命令: ** 查看當前系統的網卡信息:ifconfig 查看與某臺機器的連接情況:ping 查看當前系統的端口使用:netstat -an shutdown : shutdown -h now : 指定現在立即關機; shutdown +5 "System will shutdown after 5 minutes" :指定5分鐘后關機,同時送出警告信息給登入用戶。 reboot : reboot : 重開機。reboot -w : 做個重開機的模擬(只有紀錄并不會真的重開機)。 # Mybatis ## 什么是Mybatis Mybatis是一個半ORM(對象關系映射)框架,它內部封裝了JDBC,開發時只需要關注SQL語句本身,不需要花費精力去處理加載驅動、創建連接、創建statement等繁雜的過程。程序員直接編寫原生態sql,可以嚴格控制sql執行性能,靈活度高。 MyBatis 可以使用 XML 或注解來配置和映射原生信息,將 POJO映射成數據庫中的記錄,避免了幾乎所有的 JDBC 代碼和手動設置參數以及獲取結果集。 通過xml 文件或注解的方式將要執行的各種 statement 配置起來,并通過java對象和 statement中sql的動態參數進行映射生成最終執行的sql語句,最后由mybatis框架執行sql并將結果映射為java對象并返回。(從執行sql到返回result的過程)。 ## Mybaits的優點 基于SQL語句編程,相當靈活,不會對應用程序或者數據庫的現有設計造成任何影響,SQL寫在XML里,解除sql與程序代碼的耦合,便于統一管理;提供XML標簽,支持編寫動態SQL語句,并可重用。 與JDBC相比,減少了50%以上的代碼量,消除了JDBC大量冗余的代碼,不需要手動開關連接; 很好的與各種數據庫兼容(因為MyBatis使用JDBC來連接數據庫,所以只要JDBC支持的數據庫MyBatis都支持) 能夠與Spring很好的集成; 提供映射標簽,支持對象與數據庫的ORM字段關系映射;提供對象關系映射標簽,支持對象關系組件維護。 ## MyBatis框架的缺點 SQL語句的編寫工作量較大,尤其當字段多、關聯表多時,對開發人員編寫SQL語句的功底有一定要求。 SQL語句依賴于數據庫,導致數據庫移植性差,不能隨意更換數據庫。 ## MyBatis與Hibernate有哪些不同 Mybatis和hibernate不同,它不完全是一個ORM框架,因為MyBatis需要程序員自己編寫Sql語句。 Mybatis直接編寫原生態sql,可以嚴格控制sql執行性能,靈活度高,非常適合對關系數據模型要求不高的軟件開發,因為這類軟件需求變化頻繁,一但需求變化要求迅速輸出成果。但是靈活的前提是mybatis無法做到數據庫無關性,如果需要實現支持多種數據庫的軟件,則需要自定義多套sql映射文件,工作量大。? Hibernate對象/關系映射能力強,數據庫無關性好,對于關系模型要求高的軟件,如果用hibernate開發可以節省很多代碼,提高效率。 ## #{}和${}的區別是什么 :#{}是預編譯處理,${}是字符串替換。 Mybatis在處理#{}時,會將sql中的#{}替換為?號,調用PreparedStatement的set方法來賦值; Mybatis在處理${}時,就是把${}替換成變量的值。 使用#{}可以有效的防止SQL注入,提高系統安全性。 ## 通常一個Xml映射文件,都會寫一個Dao接口與之對應,請問,這個Dao接口的工作原理是什么?Dao接口里的方法,參數不同時,方法能重載嗎 Dao接口即Mapper接口。接口的全限名,就是映射文件中的namespace的值;接口的方法名,就是映射文件中Mapper的Statement的id值;接口方法內的參數,就是傳遞給sql的參數。 Mapper接口是沒有實現類的,當調用接口方法時,接口全限名+方法名拼接字符串作為key值,可唯一定位一個MapperStatement。在Mybatis中,每一個、、、標簽,都會被解析為一個MapperStatement對象。 舉例:com.mybatis3.mappers.StudentDao.findStudentById,可以唯一找到namespace為com.mybatis3.mappers.StudentDao下面 id 為 findStudentById 的 MapperStatement。 Mapper接口里的方法,是不能重載的,因為是使用 全限名+方法名 的保存和尋找策略。Mapper 接口的工作原理是JDK動態代理,Mybatis運行時會使用JDK動態代理為Mapper接口生成代理對象proxy,代理對象會攔截接口方法,轉而執行MapperStatement所代表的sql,然后將sql執行結果返回。 ## Mybatis是如何進行分頁的?分頁插件的原理是什么 Mybatis使用RowBounds對象進行分頁,它是針對ResultSet結果集執行的內存分頁,而非物理分頁。可以在sql內直接書寫帶有物理分頁的參數來完成物理分頁功能,也可以使用分頁插件來完成物理分頁。 分頁插件的基本原理是使用Mybatis提供的插件接口,實現自定義插件,在插件的攔截方法內攔截待執行的sql,然后重寫sql,根據dialect方言,添加對應的物理分頁語句和物理分頁參數。 ## Mybatis動態sql有什么用?執行原理?有哪些動態sql Mybatis動態sql可以在Xml映射文件內,以標簽的形式編寫動態sql,執行原理是根據表達式的值 完成邏輯判斷并動態拼接sql的功能。 Mybatis提供了9種動態sql標簽:trim | where | set | foreach | if | choose | when | otherwise | bind。 ## 為什么說Mybatis是半自動ORM映射工具?它與全自動的區別在哪里 Hibernate屬于全自動ORM映射工具,使用Hibernate查詢關聯對象或者關聯集合對象時,可以根據對象關系模型直接獲取,所以它是全自動的。而Mybatis在查詢關聯對象或關聯集合對象時,需要手動編寫sql來完成,所以,稱之為半自動ORM映射工具。 ## Mybatis的一級、二級緩存 一級緩存: 基于 PerpetualCache 的 HashMap 本地緩存,其存儲作用域為 Session,當 Session flush 或 close 之后,該 Session 中的所有 Cache 就將清空,默認打開一級緩存。 二級緩存與一級緩存其機制相同,默認也是采用 PerpetualCache,HashMap 存儲,不同在于其存儲作用域為 Mapper(Namespace),并且可自定義存儲源,如 Ehcache。默認不打開二級緩存,要開啟二級緩存,使用二級緩存屬性類需要實現Serializable序列化接口(可用來保存對象的狀態),可在它的映射文件中配置 ; 對于緩存數據更新機制,當某一個作用域(一級緩存 Session/二級緩存Namespaces)的進行了C/U/D 操作后,默認該作用域下所有 select 中的緩存將被 clear。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看