理想汽車的Java 後端面經來了

帶你聊技術發表於2024-01-18

來源:小林coding

一線城市由於限車油牌,很多我身邊的朋友,買的第一輛車都是電車,基本上人均特斯拉。國內也有很多優秀的新能源車企,理想、小鵬、比亞迪、蔚來等等,最近小米也入局這個賽道。

這些新能源車企中,發現理想的校招薪資特別高,不少拿到理想汽車開發崗 offer 的同學跟我反饋開到了 30-35k x 14(總包 40-50w),比網際網路大廠都多很多,問我要不要接?

理想汽車的Java 後端面經來了

演算法崗更是離譜,都接近 40k 了(總包 55w+)。

理想汽車的Java 後端面經來了
理想汽車的Java 後端面經來了

看了下理想的財報才知道,原來理想汽車 2023 的銷售非常好,所以在開發崗位上擴招了很多,同時給校招生開的薪資非常的給力。

話說回來,理想開這麼高薪資,面試難度如何呢?

之前分享了很多網際網路公司後端面經,這次給大家分享一位同學面試理想汽車的Java 後端面經,這個面經還是比較經典,基本後端的知識都問了遍。

我也把問到的知識點,羅列了一下

  • Java:執行緒池、垃圾回收、juc、spring aop
  • MySQL:索引失效
  • Redis:快取三兄弟、布隆過濾器
  • Kafka:topic、分割槽、消費執行緒的關係
  • 作業系統:執行緒間通訊、socket 程式設計
  • 網路:訪問網站到顯示的全流程

MySQL

索引失效的場景知道哪些?

對索引使用左或者左右模糊匹配,會索引失效

當我們使用左或者左右模糊匹配的時候,也就是 like %xx 或者 like %xx% 這兩種方式都會造成索引失效。

比如下面的 like 語句,查詢 name 字尾為「林」的使用者,執行計劃中的 type=ALL 就代表了全表掃描,而沒有走索引。

// name 欄位為二級索引
select * from t_user where name like '%林';
理想汽車的Java 後端面經來了

對索引使用函式,會索引失效

有時候我們會用一些 MySQL 自帶的函式來得到我們想要的結果,這時候要注意了,如果查詢條件中對索引欄位使用函式,就會導致索引失效。

比如下面這條語句查詢條件中對 name 欄位使用了 LENGTH 函式,執行計劃中的 type=ALL,代表了全表掃描:

// name 為二級索引
select * from t_user where length(name)=6;
理想汽車的Java 後端面經來了

對索引進行表示式計算,會索引失效

在查詢條件中對索引進行表示式計算,也是無法走索引的。

比如,下面這條查詢語句,執行計劃中 type = ALL,說明是透過全表掃描的方式查詢資料的:

explain select * from t_user where id + 1 = 10;
理想汽車的Java 後端面經來了

對索引隱式型別轉換,會索引失效

如果索引欄位是字串型別,但是在條件查詢中,輸入的引數是整型的話,你會在執行計劃的結果發現這條語句會走全表掃描。

我在原本的 t_user 表增加了 phone 欄位,是二級索引且型別是 varchar。

理想汽車的Java 後端面經來了

然後我在條件查詢中,用整型作為輸入引數,此時執行計劃中 type = ALL,所以是透過全表掃描來查詢資料的。

select * from t_user where phone = 1300000001;
理想汽車的Java 後端面經來了

這是因為 phone 欄位為字串,所以 MySQL 要會自動把字串轉為數字,所以這條語句相當於:

select * from t_user where CAST(phone AS signed int) = 1300000001;

可以看到,CAST 函式是作用在了 phone 欄位,而 phone 欄位是索引,也就是對索引使用了函式!而前面我們也說了,對索引使用函式是會導致索引失效的

聯合索引非最左匹配,會索引失效

聯合索引要能正確使用需要遵循最左匹配原則,也就是按照最左優先的方式進行索引的匹配。

比如,如果建立了一個 (a, b, c) 聯合索引,如果查詢條件是以下這幾種,就可以匹配上聯合索引:

  • where a=1;
  • where a=1 and b=2 and c=3;
  • where a=1 and b=2;

需要注意的是,因為有查詢最佳化器,所以 a 欄位在 where 子句的順序並不重要。

但是,如果查詢條件是以下這幾種,因為不符合最左匹配原則,所以就無法匹配上聯合索引,聯合索引就會失效:

  • where b=2;
  • where c=3;
  • where b=2 and c=3;

Redis

什麼是快取雪崩、快取擊穿和快取穿透?怎麼解決?

  • 快取雪崩:當大量快取資料在同一時間過期(失效)或者 Redis 故障當機時,如果此時有大量的使用者請求,都無法在 Redis 中處理,於是全部請求都直接訪問資料庫,從而導致資料庫的壓力驟增,嚴重的會造成資料庫當機,從而形成一系列連鎖反應,造成整個系統崩潰,這就是快取雪崩的問題。
理想汽車的Java 後端面經來了
  • 快取擊穿:如果快取中的某個熱點資料過期了,此時大量的請求訪問了該熱點資料,就無法從快取中讀取,直接訪問資料庫,資料庫很容易就被高併發的請求沖垮,這就是快取擊穿的問題。
理想汽車的Java 後端面經來了
  • 快取穿透:當使用者訪問的資料,既不在快取中,也不在資料庫中,導致請求在訪問快取時,發現快取缺失,再去訪問資料庫時,發現資料庫中也沒有要訪問的資料,沒辦法構建快取資料,來服務後續的請求。那麼當有大量這樣的請求到來時,資料庫的壓力驟增,這就是快取穿透的問題。
理想汽車的Java 後端面經來了

快取雪崩解決方案:

  • 均勻設定過期時間:如果要給快取資料設定過期時間,應該避免將大量的資料設定成同一個過期時間。我們可以在對快取資料設定過期時間時,給這些資料的過期時間加上一個隨機數,這樣就保證資料不會在同一時間過期。
  • 互斥鎖:當業務執行緒在處理使用者請求時,如果發現訪問的資料不在 Redis 裡,就加個互斥鎖,保證同一時間內只有一個請求來構建快取(從資料庫讀取資料,再將資料更新到 Redis 裡),當快取構建完成後,再釋放鎖。未能獲取互斥鎖的請求,要麼等待鎖釋放後重新讀取快取,要麼就返回空值或者預設值。實現互斥鎖的時候,最好設定超時時間,不然第一個請求拿到了鎖,然後這個請求發生了某種意外而一直阻塞,一直不釋放鎖,這時其他請求也一直拿不到鎖,整個系統就會出現無響應的現象。
  • 後臺更新快取:業務執行緒不再負責更新快取,快取也不設定有效期,而是讓快取“永久有效”,並將更新快取的工作交由後臺執行緒定時更新

快取擊穿解決方案:

  • 互斥鎖方案,保證同一時間只有一個業務執行緒更新快取,未能獲取互斥鎖的請求,要麼等待鎖釋放後重新讀取快取,要麼就返回空值或者預設值。
  • 不給熱點資料設定過期時間,由後臺非同步更新快取,或者在熱點資料準備要過期前,提前通知後臺執行緒更新快取以及重新設定過期時間;

快取穿透解決方案:

  • 非法請求的限制:當有大量惡意請求訪問不存在的資料的時候,也會發生快取穿透,因此在 API 入口處我們要判斷求請求引數是否合理,請求引數是否含有非法值、請求欄位是否存在,如果判斷出是惡意請求就直接返回錯誤,避免進一步訪問快取和資料庫。
  • 快取空值或者預設值:當我們線上業務發現快取穿透的現象時,可以針對查詢的資料,在快取中設定一個空值或者預設值,這樣後續請求就可以從快取中讀取到空值或者預設值,返回給應用,而不會繼續查詢資料庫。
  • 布隆過濾器:我們可以在寫入資料庫資料時,使用布隆過濾器做個標記,然後在使用者請求到來時,業務執行緒確認快取失效後,可以透過查詢布隆過濾器快速判斷資料是否存在,如果不存在,就不用透過查詢資料庫來判斷資料是否存在。即使發生了快取穿透,大量請求只會查詢 Redis 和布隆過濾器,而不會查詢資料庫,保證了資料庫能正常執行,Redis 自身也是支援布隆過濾器的。

布隆過濾器原理是什麼?

布隆過濾器由「初始值都為 0 的點陣圖陣列」和「 N 個雜湊函式」兩部分組成。當我們在寫入資料庫資料時,在布隆過濾器裡做個標記,這樣下次查詢資料是否在資料庫時,只需要查詢布隆過濾器,如果查詢到資料沒有被標記,說明不在資料庫中。

布隆過濾器會透過 3 個操作完成標記:

  • 第一步,使用 N 個雜湊函式分別對資料做雜湊計算,得到 N 個雜湊值;
  • 第二步,將第一步得到的 N 個雜湊值對點陣圖陣列的長度取模,得到每個雜湊值在點陣圖陣列的對應位置。
  • 第三步,將每個雜湊值在點陣圖陣列的對應位置的值設定為 1;

舉個例子,假設有一個點陣圖陣列長度為 8,雜湊函式 3 個的布隆過濾器。

理想汽車的Java 後端面經來了

在資料庫寫入資料 x 後,把資料 x 標記在布隆過濾器時,資料 x 會被 3 個雜湊函式分別計算出 3 個雜湊值,然後在對這 3 個雜湊值對 8 取模,假設取模的結果為 1、4、6,然後把點陣圖陣列的第 1、4、6 位置的值設定為 1。當應用要查詢資料 x 是否資料庫時,透過布隆過濾器只要查到點陣圖陣列的第 1、4、6 位置的值是否全為 1,只要有一個為 0,就認為資料 x 不在資料庫中

布隆過濾器由於是基於雜湊函式實現查詢的,高效查詢的同時存在雜湊衝突的可能性,比如資料 x 和資料 y 可能都落在第 1、4、6 位置,而事實上,可能資料庫中並不存在資料 y,存在誤判的情況。

所以,查詢布隆過濾器說資料存在,並不一定證明資料庫中存在這個資料,但是查詢到資料不存在,資料庫中一定就不存在這個資料

作業系統

執行緒間有哪些通訊方式?

  • 共享記憶體:執行緒可以透過訪問共享的記憶體區域來進行資料交換和共享。Linux提供了共享記憶體的機制,可以使用shmget()shmat()等函式進行共享記憶體的建立和對映。

  • 訊號量:執行緒可以使用訊號量來進行同步和互斥操作。Linux提供了訊號量機制,可以使用sem_init()sem_wait()sem_post()等函式來操作訊號量。

  • 互斥鎖:執行緒可以使用互斥鎖來實現對共享資源的互斥訪問。Linux提供了互斥鎖機制,可以使用pthread_mutex_init()pthread_mutex_lock()pthread_mutex_unlock()等函式來操作互斥鎖。

  • 條件變數:執行緒可以使用條件變數來等待和通知特定的條件。Linux提供了條件變數機制,可以使用pthread_cond_init()pthread_cond_wait()pthread_cond_signal()等函式來操作條件變數。

  • 管道:執行緒可以使用管道進行簡單的資料傳輸。Linux提供了管道機制,可以使用pipe()函式來建立管道,並使用read()write()函式進行資料的讀寫。

有了解過Socket網路套接字嗎?

理想汽車的Java 後端面經來了
  • 服務端和客戶端初始化 socket,得到檔案描述符;
  • 服務端呼叫 bind,將 socket 繫結在指定的 IP 地址和埠;
  • 服務端呼叫 listen,進行監聽;
  • 服務端呼叫 accept,等待客戶端連線;
  • 客戶端呼叫 connect,向服務端的地址和埠發起連線請求;
  • 服務端 accept 返回用於傳輸的 socket 的檔案描述符;
  • 客戶端呼叫 write 寫入資料;服務端呼叫 read 讀取資料;
  • 客戶端斷開連線時,會呼叫 close,那麼服務端 read 讀取資料的時候,就會讀取到了 EOF,待處理完資料後,服務端呼叫 close,表示連線關閉。

這裡需要注意的是,服務端呼叫 accept 時,連線成功了會返回一個已完成連線的 socket,後續用來傳輸資料。

所以,監聽的 socket 和真正用來傳送資料的 socket,是「兩個」 socket,一個叫作監聽 socket,一個叫作已完成連線 socket

成功連線建立之後,雙方開始透過 read 和 write 函式來讀寫資料,就像往一個檔案流裡面寫東西一樣。

網路

鍵入網址到瀏覽器顯示出來的過程?

應用層DNS解析,傳輸層TCP連線,網路層IP,資料鏈路MAC,真實物理層,接收到之後再一層層扒皮。

理想汽車的Java 後端面經來了

更詳細傳輸層->網路層->資料鏈路層->路由器的過程,看圖解網路->基礎篇->鍵入網址到網頁顯示期間發生了什麼?。

理想汽車的Java 後端面經來了

Java

Java中執行緒池有哪些?

  • ScheduledThreadPool:可以設定定期的執行任務,它支援定時或週期性執行任務,比如每隔 10 秒鐘執行一次任務,我透過這個實現類設定定期執行任務的策略。
  • FixedThreadPool:它的核心執行緒數和最大執行緒數是一樣的,所以可以把它看作是固定執行緒數的執行緒池,它的特點是執行緒池中的執行緒數除了初始階段需要從 0 開始增加外,之後的執行緒數量就是固定的,就算任務數超過執行緒數,執行緒池也不會再建立更多的執行緒來處理任務,而是會把超出執行緒處理能力的任務放到任務佇列中進行等待。而且就算任務佇列滿了,到了本該繼續增加執行緒數的時候,由於它的最大執行緒數和核心執行緒數是一樣的,所以也無法再增加新的執行緒了。
  • CachedThreadPool:可以稱作可快取執行緒池,它的特點在於執行緒數是幾乎可以無限增加的(實際最大可以達到 Integer.MAX_VALUE,為 2^31-1,這個數非常大,所以基本不可能達到),而當執行緒閒置時還可以對執行緒進行回收。也就是說該執行緒池的執行緒數量不是固定不變的,當然它也有一個用於儲存提交任務的佇列,但這個佇列是 SynchronousQueue,佇列的容量為0,實際不儲存任何任務,它只負責對任務進行中轉和傳遞,所以效率比較高。
  • SingleThreadExecutor:它會使用唯一的執行緒去執行任務,原理和 FixedThreadPool 是一樣的,只不過這裡執行緒只有一個,如果執行緒在執行任務的過程中發生異常,執行緒池也會重新建立一個執行緒來執行後續的任務。這種執行緒池由於只有一個執行緒,所以非常適合用於所有任務都需要按被提交的順序依次執行的場景,而前幾種執行緒池不一定能夠保障任務的執行順序等於被提交的順序,因為它們是多執行緒並行執行的。
  • SingleThreadScheduledExecutor:它實際和 ScheduledThreadPool 執行緒池非常相似,它只是 ScheduledThreadPool 的一個特例,內部只有一個執行緒。

執行緒池淘汰策略有哪些?

當執行緒池的任務佇列滿了之後,執行緒池會執行指定的拒絕策略來應對,常用的四種拒絕策略包括:CallerRunsPolicy、AbortPolicy、DiscardPolicy、DiscardOldestPolicy,此外,還可以透過實現RejectedExecutionHandler介面來自定義拒絕策略。

四種預置的拒絕策略:

  • CallerRunsPolicy,使用執行緒池的呼叫者所在的執行緒去執行被拒絕的任務,除非執行緒池被停止或者執行緒池的任務佇列已有空缺。
  • AbortPolicy,直接丟擲一個任務被執行緒池拒絕的異常。
  • DiscardPolicy,不做任何處理,靜默拒絕提交的任務。
  • DiscardOldestPolicy,拋棄最老的任務,然後執行該任務。
  • 自定義拒絕策略,透過實現介面可以自定義任務拒絕策略。

GC是什麼?

GC 是垃圾收集的意思,記憶體處理是程式設計人員容易出現問題的地方,忘記或者錯誤的記憶體回收會導致程式或系統的不穩定甚至崩潰。

Java 虛擬機器提供的 GC 功能可以自動監測物件是否超過作用域從而達到自動回收記憶體的目的,Java 語言沒有提供釋放已分配記憶體的顯示操作方法。Java 程式設計師不用擔心記憶體管理, 因為垃圾收集器會自動進行管理。

說一下G1垃圾回收器?

G1(Garbage First) 垃圾收集器,是關注最小時延的垃圾回收器,也同樣適合大尺寸堆記憶體的垃圾收集,官方推薦選擇使用 G1 來替代 CMS 。

G1最大的特點是引入分割槽的思路,弱化了分代的概念。合理利用垃圾收集各個週期的資源,解決了其他收集器、甚至 CMS 的眾多缺陷。

G1 相比 CMS的改進主要是這幾個方面:

  • 演算法:G1 基於標記--整理演算法, 不會產生空間碎片,在分配大物件時,不會因無法得到連續的空間,而提前觸發一次 FULL GC 。
  • 停頓時間可控:G1可以透過設定預期停頓時間(Pause Time)來控制垃圾收集時間避免應用雪崩現象。
  • 並行與併發:G1 能更充分的利用 CPU 多核環境下的硬體優勢,來縮短 stop the world 的停頓時間。

G1 收集器的主要應用在多 CPU 大記憶體的服務中,在滿足高吞吐量的同時,儘可能的滿足垃圾回收時的暫停時間。在以下場景中,G1 更適合:

  • 服務端多核 CPU、JVM 記憶體佔用較大的應用(至少大於4G);
  • 應用在執行過程中,會產生大量記憶體碎片、需要經常壓縮空間;
  • 想要更可控、可預期的 GC 停頓週期,防止高併發下應用雪崩現象。

瞭解volatile嗎?

volatile關鍵字保證了兩個性質:

  • 可見性:可見性是指當多個執行緒訪問同一個變數時,一個執行緒修改了這個變數的值,其他執行緒能夠立即看得到修改的值。
  • 有序性:對一個volatile變數的寫操作,執行在任意後續對這個volatile變數的讀操作之前。

volatile 彙編是怎麼實現的?

對於JVM的記憶體屏障實現中,也採取了記憶體屏障。JVM的記憶體屏障有四種,我們來看一下這四種屏障和他們的作用:

LoadLoad屏障:對於這樣的語句

第一大段讀資料指令; 
LoadLoad; 
第二大段讀資料指令;

LoadLoad指令作用:在第二大段讀資料指令被訪問前,保證第一大段讀資料指令執行完畢

StoreStore屏障:對於這樣的語句

第一大段寫資料指令; 
StoreStore; 
第二大段寫資料指令;

StoreStore指令作用:在第二大段寫資料指令被訪問前,保證第一大段寫資料指令執行完畢

LoadStore屏障:對於這樣的語句

第一大段讀資料指令; 
LoadStore; 
第二大段寫資料指令;

LoadStore指令作用:在第二大段寫資料指令被訪問前,保證第一大段讀資料指令執行完畢。

StoreLoad屏障:對於這樣的語句

第一大段寫資料指令; 
StoreLoad; 
第二大段讀資料指令;

StoreLoad指令作用:在第二大段讀資料指令被訪問前,保證第一大段寫資料指令執行完畢。

針對volatile變數,JVM採用的記憶體屏障是:

  1. 針對volatile修飾變數的寫操作:在寫操作前插入StoreStore屏障,在寫操作後插入StoreLoad屏障;
  2. 針對volatile修飾變數的讀操作:在每個volatile讀操作前插入LoadLoad屏障,在讀操作後插入LoadStore屏障;

透過這種方式,就可以保證被volatile修飾的變數具有執行緒間的可見性和禁止指令重排序的功能了。

Synchronized 和 ReentrantLock 有什麼區別?

主要區別有以下 5 個:

  • 用法不同:synchronized 可以用來修飾普通方法、靜態方法和程式碼塊,而 ReentrantLock 只能用於程式碼塊。
  • 獲取鎖和釋放鎖的機制不同:synchronized 是自動加鎖和釋放鎖的,而 ReentrantLock 需要手動加鎖和釋放鎖。
  • 鎖型別不同:synchronized 是非公平鎖,而 ReentrantLock 預設為非公平鎖,也可以手動指定為公平鎖。
  • 響應中斷不同:ReentrantLock 可以響應中斷,解決死鎖的問題,而 synchronized 不能響應中斷。
  • 底層實現不同:synchronized 是 JVM 層面透過監視器實現的,而 ReentrantLock 是基於 AQS 實現的。

Kafka

對Kafka有什麼瞭解嗎?

Kafka特點如下:

  • 高吞吐量、低延遲:kafka每秒可以處理幾十萬條訊息,它的延遲最低只有幾毫秒,每個topic可以分多個partition, consumer group 對partition進行consume操作。
  • 可擴充套件性:kafka叢集支援熱擴充套件
  • 永續性、可靠性:訊息被持久化到本地磁碟,並且支援資料備份防止資料丟失
  • 容錯性:允許叢集中節點失敗(若副本數量為n,則允許n-1個節點失敗)
  • 高併發:支援數千個客戶端同時讀寫

如果有一個消費主題topic,有一個消費組group,topic有10個分割槽,消費執行緒數和分割槽數的關係是怎麼樣的?

topic下的一個分割槽只能被同一個consumer group下的一個consumer執行緒來消費,但反之並不成立,即一個consumer執行緒可以消費多個分割槽的資料,比如Kafka提供的ConsoleConsumer,預設就只是一個執行緒來消費所有分割槽的資料。

理想汽車的Java 後端面經來了

所以,分割槽數決定了同組消費者個數的上限

如果你的分割槽數是N,那麼最好執行緒數也保持為N,這樣通常能夠達到最大的吞吐量。超過N的配置只是浪費系統資源,因為多出的執行緒不會被分配到任何分割槽。

Spring

Spring AOP的概念瞭解嗎?

Spring AOP是Spring框架中的一個重要模組,用於實現面向切面程式設計。

可以把Spring AOP看作是對Spring的補充,它使得Spring不需要EJB就能提供宣告式事務管理;或者 使用Spring AOP框架的全部功能來實現自定義的方面。

AOP概念:

  • 方面(Aspect):一個關注點的模組化,這個關注點實現可能 另外橫切多個物件。事務管理是J2EE應用中一個很好的橫切關注點例子。方面用Spring的 Advisor或攔截器實現。
  • 連線點(Joinpoint): 程式執行過程中明確的點,如方法的調 用或特定的異常被丟擲。
  • 通知(Advice): 在特定的連線點,AOP框架執行的動作。各種類 型的通知包括“around”、“before”和“throws”通知。通知型別將在下面討論。許多AOP框架 包括Spring都是以攔截器做通知模型,維護一個“圍繞”連線點的攔截器 鏈。
  • 切入點(Pointcut): 指定一個通知將被引發的一系列連線點 的集合。AOP框架必須允許開發者指定切入點:例如,使用正規表示式。
  • 引入(Introduction): 新增方法或欄位到被通知的類。Spring允許引入新的介面到任何被通知的物件。例如,你可以使用一個引入使任何物件實現 IsModified介面,來簡化快取。
  • 目標物件(Target Object): 包含連線點的物件。也被稱作 被通知被代理物件。
  • AOP代理(AOP Proxy): AOP框架建立的物件,包含通知。在Spring中,AOP代理可以是JDK動態代理或者CGLIB代理。
  • 織入(Weaving): 組裝方面來建立一個被通知物件。這可以在編譯時 完成(例如使用AspectJ編譯器),也可以在執行時完成。Spring和其他純Java AOP框架一樣, 在執行時完成織入。

AOP和OOP的關係是什麼?

AOPOOP 不是相互對立的關係,可以把 AOP 看作是彌補 OOP 的不足,以此之長、補彼之短,兩者結合使用效果最佳。

  • OOP 是針對業務實體及其屬性行為 進行抽象封裝 ,這個不難理解,例如:使用者模組、訂單模組 等。
  • AOP 是針對業務切面進行提取,它所面對的是處理過程中的某個 步驟階段 ,以達到邏輯處理過程中各部分之間低耦合性的 隔離效果 ,例如:日誌記錄、許可權驗證 等。

舉個例子,如果單純使用 OOP ,需要在日誌模組、訂單模組中進行許可權驗證、日誌記錄怎麼辦?難道要在每個方法前都加入許可權驗證、日誌記錄的程式碼嗎?那麼如果需要在每個方法前和方法後都記錄日誌怎麼辦?

這時如果使用 AOP,就可以藉助代理完成這些重複的操作,就可以不在每個方法前加入許可權驗證、日誌記錄的程式碼,降低各部分之間的耦合。

AOP底層實現是什麼?

Spring AOP的底層實現原理主要依賴於動態代理。在Spring AOP中,透過動態代理技術,可以在執行時動態地建立一個代理物件,將切面邏輯織入到目標物件的方法呼叫中。

Spring AOP主要有兩種型別的代理:基於介面的代理和基於類的代理

  • 對於基於介面的代理,Spring AOP使用JDK動態代理來實現。JDK動態代理要求目標物件實現一個或多個介面,然後透過Proxy類的靜態方法建立一個代理物件。代理物件實現了目標物件的介面,並且在方法呼叫前後新增了切面邏輯。

  • 對於基於類的代理,Spring AOP使用CGLIB(Code Generation Library)來實現。CGLIB是一個強大的程式碼生成庫,它透過繼承的方式建立一個目標物件的子類,並在子類中重寫目標物件的方法,從而實現切面邏輯的織入。

在執行時,當客戶端呼叫目標物件的方法時,實際上是呼叫了代理物件的方法。代理物件會在方法呼叫前後執行切面邏輯,並最終將方法呼叫委託給目標物件。

專案

  • 在專案中主要負責什麼?
  • 效能調優遇到了什麼瓶頸,以及是如何最佳化的?

來自 “ ITPUB部落格 ” ,連結:https://blog.itpub.net/70024922/viewspace-3004257/,如需轉載,請註明出處,否則將追究法律責任。