自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

99%的人都答錯了!Spring MVC 控制器到底是不是單例?怎么破局?

開發(fā) 前端
如果每來一個請求就 new 一個 Controller,想想服務(wù)器內(nèi)存得爆炸成啥樣子?而且 Controller 通常是無狀態(tài)的(處理邏輯、調(diào)用 Service),并不需要為每次請求新建實(shí)例。

引言

嗨大家好呀,我是小米,一個喜歡邊學(xué)邊分享,把坑踩過一遍再告訴你怎么繞開的技術(shù)宅控!

最近我在準(zhǔn)備換工作的社招面試,被問了一個超級經(jīng)典但又能坑死人的問題:

Spring MVC 的控制器(Controller)是單例的嗎?如果是,會有什么問題?怎么解決?

聽到這個問題那一刻,我表面微笑,內(nèi)心咯噔:

“完了,要是答得不清不楚,面試官又要畫叉叉了……”

好在我之前踩過這個坑,一口氣講了個通透,還得到了面試官的點(diǎn)贊!

今天我就把完整的故事和解題思路分享給大家。

記憶中的第一個坑:Controller 的單例本質(zhì)!

記得我剛學(xué) Spring MVC 的時候,腦子里想當(dāng)然覺得:

“控制器嘛,就是處理一個請求,創(chuàng)建一個對象,處理完就丟掉,多清晰!”

結(jié)果呢?查源碼一看,啪啪打臉!

實(shí)際上,Spring MVC 默認(rèn)把 Controller 當(dāng)作單例(Singleton)來管理的!

也就是說,咱們寫的這個:

圖片圖片

默認(rèn)是單例模式(Singleton Scope),由 Spring 容器托管,啟動時創(chuàng)建一個實(shí)例,整個應(yīng)用生命周期共用這一份!

所以,記住一句話:

Spring 中的 @Controller 本質(zhì)上是一個單例 Bean!

那為啥 Spring 要這么搞呢?

其實(shí)很簡單,節(jié)省資源,提高性能!

如果每來一個請求就 new 一個 Controller,想想服務(wù)器內(nèi)存得爆炸成啥樣子?而且 Controller 通常是無狀態(tài)的(處理邏輯、調(diào)用 Service),并不需要為每次請求新建實(shí)例。

所以,單例是合理的默認(rèn)選擇!

但是——

事情到這里,才剛剛開始。

單例帶來的隱患:線程安全問題!

單例 + 多線程,聽著就危險(xiǎn),對吧?

沒錯,Controller 是單例,但是 用戶請求是多線程并發(fā)的。

一旦 Controller 里寫了成員變量,而且這個成員變量又是可變的、共享的,那簡直是災(zāi)難現(xiàn)場!

比如:

圖片圖片

看著沒啥問題對吧?

但是注意啊!

  • 用戶A提交了一個orderId:1001
  • 用戶B緊接著提交了一個orderId:1002
  • 因?yàn)镃ontroller是單例的,他們共用同一個 lastOrderId!

結(jié)果:

A本來想處理自己提交的1001,結(jié)果處理到一半,lastOrderId 被 B 改成了1002……

數(shù)據(jù)錯亂、請求串臺、詭異Bug,分分鐘爆炸!

這就是典型的線程安全問題!

總結(jié)一下:

Spring MVC Controller 單例本身沒問題,問題在于如果 Controller 里保存了【有狀態(tài)的可變成員變量】,就會引發(fā)線程安全問題!

面試官想聽的:怎么解決?

好,既然知道問題了,那接下來最重要的就是——怎么解決?

思路一:保證 Controller 無狀態(tài)

  • 不要在 Controller 里寫可變的成員變量!
  • 所有數(shù)據(jù)都通過方法參數(shù)傳遞。

比如剛才的 lastOrderId,正確寫法應(yīng)該是:

圖片圖片

這樣,每個請求進(jìn)來,拿的是自己方法參數(shù)里的數(shù)據(jù),不會互相污染。

記住一句話:

Controller 要像一潭死水一樣冷靜,不要有變化,保持無狀態(tài)!

思路二:必要時改變作用域

如果業(yè)務(wù)場景確實(shí)需要保存一些請求級別的數(shù)據(jù),比如一步步流程操作,那么可以考慮改變 Bean 的作用域!

  • 使用 @Scope("request")
  • 讓每個請求有自己的 Controller 實(shí)例。

比如:

圖片圖片

加上 @Scope("request"),

Spring 會給每個請求創(chuàng)建一個新的 Controller 實(shí)例,互不影響!

當(dāng)然啦,這樣就失去了單例帶來的性能優(yōu)勢了,要慎重選擇。

大部分場景下,通過方法參數(shù)傳遞就夠了,很少需要改變作用域。

思路三:使用 ThreadLocal

如果真的需要存 per-request 數(shù)據(jù),還可以用ThreadLocal。

圖片圖片

ThreadLocal 保證每個線程有獨(dú)立副本,互不干擾。

注意,用完一定要 remove()!不然可能會導(dǎo)致內(nèi)存泄漏,尤其是在線程池環(huán)境下。

小米的社招總結(jié)答法(親測有效)

最后,總結(jié)一下,社招面試我怎么答的:

面試官問:“Spring MVC 控制器是單例的嗎?如果是,有什么問題?怎么解決?”

我答:

  • Spring MVC 的控制器默認(rèn)是單例的,由 Spring 容器管理。
  • 單例本身沒問題,但如果 Controller 里存在可變的成員變量,在多線程并發(fā)請求下會引發(fā)線程安全問題。
  • 解決辦法有:
  • 或者使用 ThreadLocal 保存每個請求的獨(dú)立數(shù)據(jù),但注意清理。
  • 必要時可以將 Controller 設(shè)為請求作用域(@Scope("request"));
  • 最推薦:保持 Controller 無狀態(tài),只通過方法參數(shù)傳遞數(shù)據(jù);

面試官點(diǎn)頭微笑,

我心里一陣狂喜,暗搓搓給自己比了個??。

總結(jié)一下今天的故事

今天我們講了:

  • Spring MVC 控制器是默認(rèn)單例的(Singleton Scope);
  • 單例會引發(fā)線程安全問題(成員變量共享導(dǎo)致數(shù)據(jù)錯亂);
  • 最好保持 Controller 無狀態(tài);
  • 特殊場景下可以使用 @Scope("request") 或 ThreadLocal;

關(guān)鍵思路:

  • Controller 要無狀態(tài),數(shù)據(jù)傳參走,線程安全穩(wěn)如老狗!
責(zé)任編輯:武曉燕 來源: 軟件求生
相關(guān)推薦

2024-05-07 13:29:00

CSS選擇器權(quán)重

2019-06-06 08:30:07

區(qū)塊鏈數(shù)字貨幣比特幣

2012-07-02 09:40:45

小米手機(jī)

2021-01-11 05:37:54

倉儲模式接口

2020-02-25 16:30:36

MD5是不是加密

2011-03-08 08:58:37

springmvc

2019-05-15 16:15:08

HTTPGETPOST

2016-09-23 15:10:10

HTTPGETPOST

2024-09-04 01:36:51

Java對象傳遞

2019-02-27 09:28:15

Redis服務(wù)器事務(wù)

2024-07-05 09:00:00

編程語言Rust開發(fā)

2025-03-11 08:20:00

C++main函數(shù)

2021-08-02 14:48:15

云電腦Windows 365華為

2016-12-23 09:04:56

大數(shù)據(jù)技術(shù)BAT

2009-01-12 11:16:58

控制器控制器行為行為結(jié)果

2022-08-04 08:23:13

顯示器色域參數(shù)

2019-12-16 09:42:38

PHP語言程序員

2019-11-17 22:58:03

Spring MVC控制器編寫

2023-10-12 08:54:20

Spring事務(wù)設(shè)置

2021-04-13 10:35:13

網(wǎng)盤存儲硬盤
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號