被快手追著項目問,我暈了...
最近不是春招開始了嘛,我多給大家分享一些互聯(lián)網(wǎng)公司的后端校招面經(jīng),給同學(xué)們學(xué)習(xí),根據(jù)面經(jīng)去復(fù)習(xí),效率 upupup!
今天分享一位同學(xué)Java后端快手實習(xí)面經(jīng),主要是針對項目涉及的技術(shù)棧去問了,同學(xué)項目用到了組件比較多,比如微服務(wù)組件、mysql、redis、es、kafaka。
所以針對每個組件都稍微問一點,所以大家簡歷所涉及的技術(shù)棧一定得掌握,不是說會使用就行。
面試考察的內(nèi)容:
- 網(wǎng)絡(luò):DNS、HTTP、UDP、Cookie
- 數(shù)據(jù)結(jié)構(gòu)與算法:數(shù)組、鏈表、棧、隊列
- 后端:mysql 日志、es 倒排索引、kafaka 消息可靠+消息不重復(fù)消息
- 微服務(wù):微服務(wù)組件、負載均衡算法、服務(wù)熔斷、服務(wù)降級
- spring:ioc、aop、循環(huán)依賴、事務(wù)、spring mvc 流程。
網(wǎng)絡(luò)
Dns基于什么協(xié)議實現(xiàn)?udp 還是 tcp?
域名解析的工作流程
DNS 基于UDP協(xié)議實現(xiàn),DNS使用UDP協(xié)議進行域名解析和數(shù)據(jù)傳輸。
為什么是udp?
因為基于UDP實現(xiàn)DNS能夠提供低延遲、簡單快速、輕量級的特性,更適合DNS這種需要快速響應(yīng)的域名解析服務(wù)。
- 低延遲: UDP是一種無連接的協(xié)議,不需要在數(shù)據(jù)傳輸前建立連接,因此可以減少傳輸時延,適合DNS這種需要快速響應(yīng)的應(yīng)用場景。
- 簡單快速: UDP相比于TCP更簡單,沒有TCP的連接管理和流量控制機制,傳輸效率更高,適合DNS這種需要快速傳輸數(shù)據(jù)的場景。
- 輕量級:UDP頭部較小,占用較少的網(wǎng)絡(luò)資源,對于小型請求和響應(yīng)來說更加輕量級,適合DNS這種頻繁且短小的數(shù)據(jù)交換。
盡管 UDP 存在丟包和數(shù)據(jù)包損壞的風(fēng)險,但在 DNS 的設(shè)計中,這些風(fēng)險是可以被容忍的。DNS 使用了一些機制來提高可靠性,例如查詢超時重傳、請求重試、緩存等,以確保數(shù)據(jù)傳輸?shù)目煽啃院驼_性。
http的特點是什么?
HTTP具有簡單、靈活、易用、通用等特點,是一種廣泛應(yīng)用于Web通信的協(xié)議。
- 基于文本: HTTP的消息是以文本形式傳輸,易于閱讀和調(diào)試,但相比二進制協(xié)議效率較低。
- 可擴展性:HTTP協(xié)議本身不限制數(shù)據(jù)的內(nèi)容和格式,可以通過擴展頭部、方法等來支持新的功能。
- 靈活性: HTTP支持不同的數(shù)據(jù)格式(如HTML、JSON、XML等),適用于多種應(yīng)用場景。
- 無狀態(tài): 每個請求之間相互獨立,服務(wù)器不會保留之前請求的狀態(tài)信息,需要通過其他手段(如Cookies、Session)來維護狀態(tài)。
http無狀態(tài)體現(xiàn)在哪?
HTTP的無狀態(tài)體現(xiàn)在每個請求之間相互獨立,服務(wù)器不會保留之前請求的狀態(tài)信息。每次客戶端向服務(wù)器發(fā)送請求時,服務(wù)器都會獨立處理該請求,不會記住之前的請求信息或狀態(tài)。
這意味著服務(wù)器無法知道兩次請求是否來自同一個客戶端,也無法知道客戶端的歷史狀態(tài),需要通過其他機制(如Cookies、Session)來維護和管理狀態(tài)信息。
Cookie 通過在請求和響應(yīng)報文中寫入 Cookie 信息來控制客戶端的狀態(tài)。
相當(dāng)于,在客戶端第一次請求后,服務(wù)器會下發(fā)一個裝有客戶信息的「小貼紙」,后續(xù)客戶端請求服務(wù)器的時候,帶上「小貼紙」,服務(wù)器就能認得了了,
Cookie 技術(shù)
Cookie和session的區(qū)別是什么?
- 存儲位置:Cookie存儲在客戶端(瀏覽器)中,而Session存儲在服務(wù)器端。
- 安全性:由于Cookie存儲在客戶端,因此容易受到安全攻擊,如跨站腳本攻擊(XSS)和跨站請求偽造(CSRF)。而Session存儲在服務(wù)器端,對客戶端不可見,相對來說更安全。
- 存儲容量:Cookie的存儲容量有限,通常為4KB左右,而Session的存儲容量較大,受限于服務(wù)器的配置。
數(shù)據(jù)結(jié)構(gòu)與算法
鏈表和數(shù)組有什么區(qū)別?
-
訪問效率:數(shù)組可以通過索引直接訪問任何位置的元素,訪問效率高,時間復(fù)雜度為O(1),而鏈表需要從頭節(jié)點開始遍歷到目標位置,訪問效率較低,時間復(fù)雜度為O(n)。
-
插入和刪除操作效率:數(shù)組插入和刪除操作可能需要移動其他元素,時間復(fù)雜度為O(n),而鏈表只需要修改指針指向,時間復(fù)雜度為O(1)。
-
緩存命中率:由于數(shù)組元素在內(nèi)存中連續(xù)存儲,可以提高CPU緩存的命中率,而鏈表節(jié)點不連續(xù)存儲,可能導(dǎo)致CPU緩存的命中率較低,頻繁的緩存失效會影響性能。
-
應(yīng)用場景:數(shù)組適合靜態(tài)大小、頻繁訪問元素的場景,而鏈表適合動態(tài)大小、頻繁插入、刪除操作的場景
如何使用兩個棧實現(xiàn)隊列?
使用兩個棧實現(xiàn)隊列的方法如下:
- 準備兩個棧,分別稱為
stackPush和stackPop。 - 當(dāng)需要入隊時,將元素壓入
stackPush棧。 - 當(dāng)需要出隊時,先判斷
stackPop是否為空,如果不為空,則直接彈出棧頂元素;如果為空,則將stackPush中的所有元素依次彈出并壓入stackPop中,然后再從stackPop中彈出棧頂元素作為出隊元素。 - 當(dāng)需要查詢隊首元素時,同樣需要先將
stackPush中的元素轉(zhuǎn)移到stackPop中,然后取出stackPop的棧頂元素但不彈出。 - 通過上述方法,可以實現(xiàn)用兩個棧來模擬隊列的先進先出(FIFO)特性。
這種方法的時間復(fù)雜度為O(1)的入隊操作,均攤時間復(fù)雜度為O(1)的出隊和查詢隊首元素操作。
以下是使用兩個棧實現(xiàn)隊列的Java代碼示例:
import java.util.Stack;
class MyQueue {
private Stack<Integer> stackPush;
private Stack<Integer> stackPop;
public MyQueue() {
stackPush = new Stack<>();
stackPop = new Stack<>();
}
public void push(int x) {
stackPush.push(x);
}
public int pop() {
if (stackPop.isEmpty()) {
while (!stackPush.isEmpty()) {
stackPop.push(stackPush.pop());
}
}
return stackPop.pop();
}
public int peek() {
if (stackPop.isEmpty()) {
while (!stackPush.isEmpty()) {
stackPop.push(stackPush.pop());
}
}
return stackPop.peek();
}
public boolean empty() {
return stackPush.isEmpty() && stackPop.isEmpty();
}
}
// 測試代碼
public class Main {
public static void main(String[] args) {
MyQueue queue = new MyQueue();
queue.push(1);
queue.push(2);
System.out.println(queue.peek()); // 輸出 1
System.out.println(queue.pop()); // 輸出 1
System.out.println(queue.empty()); // 輸出 false
}
}
后端組件
MySQL的三大日志說一下,分別應(yīng)用場景是什么?
MySQL的三大日志包括:redolog、binlog和undolog。
- redolog:主要用于保證事務(wù)的持久性(ACID特性中的D:持久性)。當(dāng)數(shù)據(jù)庫發(fā)生故障時,通過重做日志可以將未提交的事務(wù)重新執(zhí)行,確保數(shù)據(jù)的一致性。
- binlog:用于主從復(fù)制、數(shù)據(jù)恢復(fù)和數(shù)據(jù)備份。二進制日志記錄了所有對數(shù)據(jù)庫的更改操作,包括數(shù)據(jù)更新、插入、刪除等,以便在主從復(fù)制時同步數(shù)據(jù)或進行數(shù)據(jù)恢復(fù)和備份。
- undolog:主要用于事務(wù)的回滾操作。當(dāng)事務(wù)執(zhí)行過程中發(fā)生異常或需要回滾時,回滾日志記錄了事務(wù)的操作信息,可以用于撤銷事務(wù)對數(shù)據(jù)庫的修改,實現(xiàn)事務(wù)的原子性。
ElasticSearch如何進行全文檢索的?
主要是利用了倒排索引的查詢結(jié)構(gòu),倒排索引是一種用于快速搜索的數(shù)據(jù)結(jié)構(gòu),它將文檔中的每個單詞與包含該單詞的文檔進行關(guān)聯(lián)。通常,倒排索引由單詞(terms)和包含這些單詞的文檔(document)列表組成。
如何理解倒排索引呢?假如現(xiàn)有三份數(shù)據(jù)文檔,文檔的內(nèi)容如下分別是:
通過分詞器將每個文檔的內(nèi)容域拆分成單獨的「詞詞匯」:
然后再構(gòu)建從詞匯到文檔ID的映射,就形成了倒排索引。
當(dāng)進行搜索時,系統(tǒng)只需查找倒排索引中包含搜索關(guān)鍵詞的文檔列表,比如用戶輸入"秋水",通過倒排索引,可以快速的找到含有"秋水"的文檔是id為 1,2 的文檔,從而達到快速的全文檢索的目的。
了解過 es 分詞器有哪些?
常見的分詞器如下:
-
standard 默認分詞器,對單個字符進行切分,查全率高,準確度較低
-
IK 分詞器 ik_max_word:查全率與準確度較高,性能也高,是業(yè)務(wù)中普遍采用的中文分詞器
-
IK 分詞器 ik_smart:切分力度較大,準確度與查全率不高,但是查詢性能較高
-
Smart Chinese 分詞器:查全率與準確率性能較高
-
hanlp 中文分詞器:切分力度較大,準確度與查全率不高,但是查詢性能較高
-
Pinyin 分詞器:針對漢字拼音進行的分詞器,與上面介紹的分詞器稍有不同,在用拼音進行查詢時查全率準確度較高
分詞器比較
Kafka如何保證消息不丟失?
使用一個消息隊列,其實就分為三大塊:生產(chǎn)者、中間件、消費者,所以要保證消息就是保證三個環(huán)節(jié)都不能丟失數(shù)據(jù)。
圖片
- 消息生產(chǎn)階段:生產(chǎn)者會不會丟消息,取決于生產(chǎn)者對于異常情況的處理是否合理。從消息被生產(chǎn)出來,然后提交給 MQ 的過程中,只要能正常收到 ( MQ 中間件) 的 ack 確認響應(yīng),就表示發(fā)送成功,所以只要處理好返回值和異常,如果返回異常則進行消息重發(fā),那么這個階段是不會出現(xiàn)消息丟失的。
- 消息存儲階段:Kafka 在使用時是部署一個集群,生產(chǎn)者在發(fā)布消息時,隊列中間件通常會寫「多個節(jié)點」,也就是有多個副本,這樣一來,即便其中一個節(jié)點掛了,也能保證集群的數(shù)據(jù)不丟失。
- 消息消費階段:消費者接收消息+消息處理之后,才回復(fù) ack 的話,那么消息階段的消息不會丟失。不能收到消息就回 ack,否則可能消息處理中途掛掉了,消息就丟失了。
Kafka如何保證消息不重復(fù)消費?
導(dǎo)致重復(fù)消費的原因可能出現(xiàn)在生產(chǎn)者,也可能出現(xiàn)在 MQ 或 消費者。
這里說的重復(fù)消費問題是指同一個數(shù)據(jù)被執(zhí)行了兩次,不單單指 MQ 中一條消息被消費了兩次,也可能是 MQ 中存在兩條一模一樣的消費。
- 生產(chǎn)者:生產(chǎn)者可能會重復(fù)推送一條數(shù)據(jù)到 MQ 中,為什么會出現(xiàn)這種情況呢?也許是一個 Controller 接口被重復(fù)調(diào)用了 2 次,沒有做接口冪等性導(dǎo)致的;也可能是推送消息到 MQ 時響應(yīng)比較慢,生產(chǎn)者的重試機制導(dǎo)致再次推送了一次消息。
- MQ:在消費者消費完一條數(shù)據(jù)響應(yīng) ack 信號消費成功時,MQ 突然掛了,導(dǎo)致 MQ 以為消費者還未消費該條數(shù)據(jù),MQ 恢復(fù)后再次推送了該條消息,導(dǎo)致了重復(fù)消費。
- 消費者:消費者已經(jīng)消費完了一條消息,正準備但是還未給 MQ 發(fā)送 ack 信號時,此時消費者掛了,服務(wù)重啟后 MQ 以為消費者還沒有消費該消息,再次推送了該條消息。
消費者怎么解決重復(fù)消費問題呢?這里提供兩種方法:
- 狀態(tài)判斷法:消費者消費數(shù)據(jù)后把消費數(shù)據(jù)記錄在 redis 中,下次消費時先到 redis 中查看是否存在該消息,存在則表示消息已經(jīng)消費過,直接丟棄消息。
- 業(yè)務(wù)判斷法:通常數(shù)據(jù)消費后都需要插入到數(shù)據(jù)庫中,使用數(shù)據(jù)庫的唯一性約束防止重復(fù)消費。每次消費直接嘗試插入數(shù)據(jù),如果提示唯一性字段重復(fù),則直接丟失消息。一般都是通過這個業(yè)務(wù)判斷的方法就可以簡單高效地避免消息的重復(fù)處理了。
微服務(wù)
你的項目用到了哪些微服務(wù)組件?
-
Eureka:服務(wù)注冊與發(fā)現(xiàn)組件,用于實現(xiàn)微服務(wù)架構(gòu)中的服務(wù)注冊和發(fā)現(xiàn)。
-
Ribbon:負載均衡組件,用于在客戶端實現(xiàn)負載均衡,提高系統(tǒng)的可用性和性能。
-
Feign:聲明式的 HTTP 客戶端組件,簡化了服務(wù)之間的調(diào)用和通信。
-
Hystrix:熔斷器組件,用于防止微服務(wù)間的故障蔓延,提高系統(tǒng)的容錯能力。
-
Zuul:API 網(wǎng)關(guān)組件,用于統(tǒng)一訪問入口、路由請求和過濾請求,提高系統(tǒng)的安全性和可維護性。
-
Config:配置中心組件,用于集中管理微服務(wù)的配置信息,實現(xiàn)配置的動態(tài)刷新。
負載均衡有哪些算法?
-
簡單輪詢:將請求按順序分發(fā)給后端服務(wù)器上,不關(guān)心服務(wù)器當(dāng)前的狀態(tài),比如后端服務(wù)器的性能、當(dāng)前的負載。
-
加權(quán)輪詢:根據(jù)服務(wù)器自身的性能給服務(wù)器設(shè)置不同的權(quán)重,將請求按順序和權(quán)重分發(fā)給后端服務(wù)器,可以讓性能高的機器處理更多的請求
-
簡單隨機:將請求隨機分發(fā)給后端服務(wù)器上,請求越多,各個服務(wù)器接收到的請求越平均
-
加權(quán)隨機:根據(jù)服務(wù)器自身的性能給服務(wù)器設(shè)置不同的權(quán)重,將請求按各個服務(wù)器的權(quán)重隨機分發(fā)給后端服務(wù)器
-
一致性哈希:根據(jù)請求的客戶端 ip、或請求參數(shù)通過哈希算法得到一個數(shù)值,利用該數(shù)值取模映射出對應(yīng)的后端服務(wù)器,這樣能保證同一個客戶端或相同參數(shù)的請求每次都使用同一臺服務(wù)器
-
最小活躍數(shù):統(tǒng)計每臺服務(wù)器上當(dāng)前正在處理的請求數(shù),也就是請求活躍數(shù),將請求分發(fā)給活躍數(shù)最少的后臺服務(wù)器
如何實現(xiàn)一直均衡給一個用戶?
可以通過「一致性哈希算法」來實現(xiàn),根據(jù)請求的客戶端 ip、或請求參數(shù)通過哈希算法得到一個數(shù)值,利用該數(shù)值取模映射出對應(yīng)的后端服務(wù)器,這樣能保證同一個客戶端或相同參數(shù)的請求每次都使用同一臺服務(wù)器。
介紹一下服務(wù)熔斷
服務(wù)熔斷是應(yīng)對微服務(wù)雪崩效應(yīng)的一種鏈路保護機制,類似股市、保險絲。
比如說,微服務(wù)之間的數(shù)據(jù)交互是通過遠程調(diào)用來完成的。服務(wù)A調(diào)用服務(wù),服務(wù)B調(diào)用服務(wù)c,某一時間鏈路上對服務(wù)C的調(diào)用響應(yīng)時間過長或者服務(wù)C不可用,隨著時間的增長,對服務(wù)C的調(diào)用也越來越多,然后服務(wù)C崩潰了,但是鏈路調(diào)用還在,對服務(wù)B的調(diào)用也在持續(xù)增多,然后服務(wù)B崩潰,隨之A也崩潰,導(dǎo)致雪崩效應(yīng)。
服務(wù)熔斷是應(yīng)對雪崩效應(yīng)的一種微服務(wù)鏈路保護機制。例如在高壓電路中,如果某個地方的電壓過高,熔斷器就會熔斷,對電路進行保護。同樣,在微服務(wù)架構(gòu)中,熔斷機制也是起著類似的作用。當(dāng)調(diào)用鏈路的某個微服務(wù)不可用或者響應(yīng)時間太長時,會進行服務(wù)熔斷,不再有該節(jié)點微服務(wù)的調(diào)用,快速返回錯誤的響應(yīng)信息。當(dāng)檢測到該節(jié)點微服務(wù)調(diào)用響應(yīng)正常后,恢復(fù)調(diào)用鏈路。
所以,服務(wù)熔斷的作用類似于我們家用的保險絲,當(dāng)某服務(wù)出現(xiàn)不可用或響應(yīng)超時的情況時,為了防止整個系統(tǒng)出現(xiàn)雪崩,暫時停止對該服務(wù)的調(diào)用。
在Spring Cloud框架里,熔斷機制通過Hystrix實現(xiàn)。Hystrix會監(jiān)控微服務(wù)間調(diào)用的狀況,當(dāng)失敗的調(diào)用到一定閾值,缺省是5秒內(nèi)20次調(diào)用失敗,就會啟動熔斷機制。
介紹一下服務(wù)降級
服務(wù)降級一般是指在服務(wù)器壓力劇增的時候,根據(jù)實際業(yè)務(wù)使用情況以及流量,對一些服務(wù)和頁面有策略的不處理或者用一種簡單的方式進行處理,從而釋放服務(wù)器資源的資源以保證核心業(yè)務(wù)的正常高效運行。
服務(wù)器的資源是有限的,而請求是無限的。在用戶使用即并發(fā)高峰期,會影響整體服務(wù)的性能,嚴重的話會導(dǎo)致宕機,以至于某些重要服務(wù)不可用。故高峰期為了保證核心功能服務(wù)的可用性,就需要對某些服務(wù)降級處理??梢岳斫鉃樯嵝”4?/p>
服務(wù)降級是從整個系統(tǒng)的負荷情況出發(fā)和考慮的,對某些負荷會比較高的情況,為了預(yù)防某些功能(業(yè)務(wù)場景)出現(xiàn)負荷過載或者響應(yīng)慢的情況,在其內(nèi)部暫時舍棄對一些非核心的接口和數(shù)據(jù)的請求,而直接返回一個提前準備好的fallback(退路)錯誤處理信息。這樣,雖然提供的是一個有損的服務(wù),但卻保證了整個系統(tǒng)的穩(wěn)定性和可用性。
Spring
Spring的IOC介紹一下
IOC:Inversion Of Control,即控制反轉(zhuǎn),是一種設(shè)計思想。在傳統(tǒng)的 Java SE 程序設(shè)計中,我們直接在對象內(nèi)部通過 new 的方式來創(chuàng)建對象,是程序主動創(chuàng)建依賴對象;
而在Spring程序設(shè)計中,IOC 是有專門的容器去控制對象。
所謂控制就是對象的創(chuàng)建、初始化、銷毀。
- 創(chuàng)建對象:原來是 new 一個,現(xiàn)在是由 Spring 容器創(chuàng)建。
- 初始化對象:原來是對象自己通過構(gòu)造器或者 setter 方法給依賴的對象賦值,現(xiàn)在是由 Spring 容器自動注入。
- 銷毀對象:原來是直接給對象賦值 null 或做一些銷毀操作,現(xiàn)在是 Spring 容器管理生命周期負責(zé)銷毀對象。
總結(jié):IOC 解決了繁瑣的對象生命周期的操作,解耦了我們的代碼。
所謂反轉(zhuǎn):其實是反轉(zhuǎn)的控制權(quán),前面提到是由 Spring 來控制對象的生命周期,那么對象的控制就完全脫離了我們的控制,控制權(quán)交給了 Spring 。這個反轉(zhuǎn)是指:我們由對象的控制者變成了 IOC 的被動控制者。
為什么依賴注入不適合使用字段注入?
字段注入可能引起的三個問題:
- 對象的外部可見性
- 可能導(dǎo)致循環(huán)依賴
- 無法設(shè)置注入的對象為final,也無法注入靜態(tài)變量
首先來看字段注入
@RestController
public class TestHandleController {
@Autowired
TestHandleService testHandleService;
public void helloTestService(){
testHandleService.hello();
}
}
字段注入的非常的簡便,通過以上代碼我們就可以輕松的使用TestHandleService類,但是如果變成下面這樣呢:
TestHandleController testHandle = new TestHandleController();
testHandle.helloTestService();
這樣執(zhí)行結(jié)果為空指針異常,這就是字段注入的第一個問題:對象的外部可見性,無法在容器外部實例化TestHandleService(例如在測試類中無法注入該組件),類和容器的耦合度過高,無法脫離容器訪問目標對象。
接下來看第二段代碼:
public class TestA(){
@Autowired
private TestB testB;
}
public class TestB(){
@Autowired
private TestA testA;
}
這段代碼在idea中不會報任何錯誤,但是當(dāng)你啟動項目時會發(fā)現(xiàn)報錯,大致意思是:創(chuàng)建Bean失敗,原因是當(dāng)前Bean已經(jīng)作為循環(huán)引用的一部分注入到了其他Bean中。
這就是字段注入的第二個問題:可能導(dǎo)致循環(huán)依賴
字段注入還有第三個問題:無法設(shè)置注入的對象為final,也無法注入靜態(tài)變量,原因是變量必須在類實例化進行初始化。
Spring的aop介紹一下
Spring AOP是Spring框架中的一個重要模塊,用于實現(xiàn)面向切面編程。
我們知道,Java 就是一門面向?qū)ο缶幊痰恼Z言,在 OOP 中最小的單元就是“Class 對象”,但是在 AOP 中最小的單元是“切面”。一個“切面”可以包含很多種類型和對象,對它們進行模塊化管理,例如事務(wù)管理。
在面向切面編程的思想里面,把功能分為兩種
- 核心業(yè)務(wù):登陸、注冊、增、刪、改、查、都叫核心業(yè)務(wù)
- 周邊功能:日志、事務(wù)管理這些次要的為周邊業(yè)務(wù)
在面向切面編程中,核心業(yè)務(wù)功能和周邊功能是分別獨立進行開發(fā),兩者不是耦合的,然后把切面功能和核心業(yè)務(wù)功能 "編織" 在一起,這就叫AOP。
AOP能夠?qū)⒛切┡c業(yè)務(wù)無關(guān),卻為業(yè)務(wù)模塊所共同調(diào)用的邏輯或責(zé)任(例如事務(wù)處理、日志管理、權(quán)限控制等)封裝起來,便于減少系統(tǒng)的重復(fù)代碼,降低模塊間的耦合度,并有利于未來的可拓展性和可維護性。
在 AOP 中有以下幾個概念:
-
AspectJ:切面,只是一個概念,沒有具體的接口或類與之對應(yīng),是 Join point,Advice 和 Pointcut 的一個統(tǒng)稱。
-
Join point : 連接點,指程序執(zhí)行過程中的一個點,例如方法調(diào)用、異常處理等。 在 Spring AOP 中,僅支持方法級別的連接點。
-
Advice:通知,即我們定義的一個切面中的橫切邏輯,有“around”,“before”和“after”三種類型。在很多的 AOP 實現(xiàn)框架中,Advice 通常作為一個攔截器,也可以包含許多個攔截器作為一條鏈路圍繞著 Join point 進行處理。
-
Pointcut:切點,用于匹配連接點,一個 AspectJ 中包含哪些 Join point 需要由 Pointcut 進行篩選。
-
Introduction:引介,讓一個切面可以聲明被通知的對象實現(xiàn)任何他們沒有真正實現(xiàn)的額外的接口。例如可以讓一個代理對象代理兩個目標類。
-
Weaving:織入,在有了連接點、切點、通知以及切面,如何將它們應(yīng)用到程序中呢?沒錯,就是織入,在切點的引導(dǎo)下,將通知邏輯插入到目標方法上,使得我們的通知邏輯在方法調(diào)用時得以執(zhí)行。
-
AOP proxy:AOP 代理,指在 AOP 實現(xiàn)框架中實現(xiàn)切面協(xié)議的對象。在 Spring AOP 中有兩種代理,分別是 JDK 動態(tài)代理和 CGLIB 動態(tài)代理。
-
Target object:目標對象,就是被代理的對象。
Spring AOP 是基于 JDK 動態(tài)代理和 Cglib 提升實現(xiàn)的,兩種代理方式都屬于運行時的一個方式,所以它沒有編譯時的一個處理,那么因此 Spring 是通過 Java 代碼實現(xiàn)的。
Spring的事務(wù),使用this調(diào)用是否生效?
不能生效。
因為Spring事務(wù)是通過代理對象來控制的,只有通過代理對象的方法調(diào)用才會應(yīng)用事務(wù)管理的相關(guān)規(guī)則。當(dāng)使用this直接調(diào)用時,是繞過了Spring的代理機制,因此不會應(yīng)用事務(wù)設(shè)置。
Spring 如何解決循環(huán)依賴問題?
循環(huán)依賴指的是兩個類中的屬性相互依賴對方:例如 A 類中有 B 屬性,B 類中有 A屬性,從而形成了一個依賴閉環(huán),如下圖。
循環(huán)依賴問題在Spring中主要有三種情況:
- 第一種:通過構(gòu)造方法進行依賴注入時產(chǎn)生的循環(huán)依賴問題。
- 第二種:通過setter方法進行依賴注入且是在多例(原型)模式下產(chǎn)生的循環(huán)依賴問題。
- 第三種:通過setter方法進行依賴注入且是在單例模式下產(chǎn)生的循環(huán)依賴問題。
只有【第三種方式】的循環(huán)依賴問題被 Spring 解決了,其他兩種方式在遇到循環(huán)依賴問題時,Spring都會產(chǎn)生異常。
Spring 解決單例模式下的setter循環(huán)依賴問題的主要方式是通過三級緩存解決循環(huán)依賴。三級緩存指的是 Spring 在創(chuàng)建 Bean 的過程中,通過三級緩存來緩存正在創(chuàng)建的 Bean,以及已經(jīng)創(chuàng)建完成的 Bean 實例。具體步驟如下:
-
實例化 Bean:Spring 在實例化 Bean 時,會先創(chuàng)建一個空的 Bean 對象,并將其放入一級緩存中。
-
屬性賦值:Spring 開始對 Bean 進行屬性賦值,如果發(fā)現(xiàn)循環(huán)依賴,會將當(dāng)前 Bean 對象提前暴露給后續(xù)需要依賴的 Bean(通過提前暴露的方式解決循環(huán)依賴)。
-
初始化 Bean:完成屬性賦值后,Spring 將 Bean 進行初始化,并將其放入二級緩存中。
-
注入依賴:Spring 繼續(xù)對 Bean 進行依賴注入,如果發(fā)現(xiàn)循環(huán)依賴,會從二級緩存中獲取已經(jīng)完成初始化的 Bean 實例。
通過三級緩存的機制,Spring 能夠在處理循環(huán)依賴時,確保及時暴露正在創(chuàng)建的 Bean 對象,并能夠正確地注入已經(jīng)初始化的 Bean 實例,從而解決循環(huán)依賴問題,保證應(yīng)用程序的正常運行。
Spring MVC的工作流程描述一下

Spring MVC的工作流程如下:
- 用戶發(fā)送請求至前端控制器DispatcherServlet
- DispatcherServlet收到請求調(diào)用處理器映射器HandlerMapping。
- 處理器映射器根據(jù)請求url找到具體的處理器,生成處理器執(zhí)行鏈HandlerExecutionChain(包括處理器對象和處理器攔截器)一并返回給DispatcherServlet。
- DispatcherServlet根據(jù)處理器Handler獲取處理器適配器HandlerAdapter執(zhí)行HandlerAdapter處理一系列的操作,如:參數(shù)封裝,數(shù)據(jù)格式轉(zhuǎn)換,數(shù)據(jù)驗證等操作
- 執(zhí)行處理器Handler(Controller,也叫頁面控制器)。
- Handler執(zhí)行完成返回ModelAndView
- HandlerAdapter將Handler執(zhí)行結(jié)果ModelAndView返回到DispatcherServlet
- DispatcherServlet將ModelAndView傳給ViewReslover視圖解析器
- ViewReslover解析后返回具體View
- DispatcherServlet對View進行渲染視圖(即將模型數(shù)據(jù)model填充至視圖中)。
- DispatcherServlet響應(yīng)用戶。
我是小富~ 下期見
·········· END ··············
在看 、 點贊 、 轉(zhuǎn)發(fā) ,是對我最大的鼓勵 。
技術(shù)書籍公眾號內(nèi)回復(fù)[ pdf ] Get 。
面試筆記、springcloud進階實戰(zhàn)PDF,公眾號內(nèi)回復(fù)[ 1222 ] Get。
