Spring的Controller是單例還是多例?怎麼保證並發的安全

2021-03-02 ImportNew

(給ImportNew加星標,提高Java技能)

轉自:riemann_

連結:http://blog.csdn.net/riemann_/article/details/97698560

答案:

controller默認是單例的,不要使用非靜態的成員變量,否則會發生數據邏輯混亂。正因為單例所以不是線程安全的。

我們下面來簡單的驗證下:

package com.riemann.springbootdemo.controller;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author riemann
 * @date 2019/07/29 22:56
 */
@Controller
public class ScopeTestController {

    private int num = 0;

    @RequestMapping("/testScope")
    public void testScope() {
        System.out.println(++num);
    }

    @RequestMapping("/testScope2")
    public void testScope2() {
        System.out.println(++num);
    }

}

我們首先訪問 http://localhost:8080/testScope,得到的答案是1;


然後我們再訪問 http://localhost:8080/testScope2,得到的答案是 2。


得到的不同的值,這是線程不安全的。

接下來我們再來給controller增加作用多例 @Scope("prototype")

package com.riemann.springbootdemo.controller;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author riemann
 * @date 2019/07/29 22:56
 */
@Controller
@Scope("prototype")
public class ScopeTestController {

    private int num = 0;

    @RequestMapping("/testScope")
    public void testScope() {
        System.out.println(++num);
    }

    @RequestMapping("/testScope2")
    public void testScope2() {
        System.out.println(++num);
    }

}

我們依舊首先訪問 http://localhost:8080/testScope,得到的答案是1;


然後我們再訪問 http://localhost:8080/testScope2,得到的答案還是 1。


相信大家不難發現 :

單例是不安全的,會導致屬性重複使用。

解決方案萬一必須要定義一個非靜態成員變量時候,則通過註解@Scope(「prototype」),將其設置為多例模式。在Controller中使用ThreadLocal變量
補充說明

spring bean作用域有以下5個:


singleton: 單例模式,當spring創建applicationContext容器的時候,spring會欲初始化所有的該作用域實例,加上lazy-init就可以避免預處理;


prototype: 原型模式,每次通過getBean獲取該bean就會新產生一個實例,創建後spring將不再對其管理;


(下面是在web項目下才用到的)

request: 搞web的大家都應該明白request的域了吧,就是每次請求都新產生一個實例,和prototype不同就是創建後,接下來的管理,spring依然在監聽;


session: 每次會話,同上;


global session: 全局的web域,類似於servlet中的application。

看完本文有收穫?請轉發分享給更多人

關注「ImportNew」,提升Java技能

好文章,我在看❤️

相關焦點

  • Spring中Bean的單例和多例簡單總結
    在Spring中,有兩個類型,一個是單例一個和多例,一個Bean同學,要麼是單例要麼是多例。不管你認不認這兩個類型,它們依然在哪裡,不吵不鬧。想要和Bean同學成為哥們,那這兩個類型務必要知道。prototype(多例)和singleton(單例)在Spring中,bean的Scope常被定義的兩種模式:prototype(多例)和singleton(單例)。singleton(單例):只有一個共享的實例存在,所有對這個bean的請求都會返回這個唯一的實例。
  • 高並發下線程安全的單例模式(最全最經典,值得收藏)
    在所有的設計模式中,單例模式是我們在項目開發中最為常見的設計模式之一,而單例模式有很多種實現方式,你是否都了解呢?高並發下如何保證單例模式的線程安全性呢?如何保證序列化後的單例對象在反序列化後任然是單例的呢?這些問題在看了本文之後都會一一的告訴你答案,趕快來閱讀吧!什麼是單例模式?
  • 深入解析單例模式的七種實現
    還有就是我們經常使用的servlet就是單例多線程的。使用單例能夠節省很多內存。如何實現單例模式呢?如果你的項目對性能沒有要求,那麼請直接使用餓漢式方法實現單例模式,既簡單又方便。但是,大部分程式設計師都是有追求的,豈能不追求性能。那麼我們看第一種方式,就是懶漢式,我們剛剛說過,懶漢式既保證了單例,又保證了性能。但是,他真的能保證單例嗎?可以確定的是:在單線程模式下,毫無問題,但在複雜的多線程模式下,會怎麼樣呢?show me code .測試用例:我們測試一下
  • Spring中獲取request的幾種方法,及其線程安全性分析
    由於在Spring MVC中,處理請求的Controller、Service等對象都是單例的,因此獲取request對象時最需要注意的問題,便是request對象是否是線程安全的:當有大量並發請求時,能否保證不同請求/線程中使用不同的request對象。這裡還有一個問題需要注意:前面所說的「在處理請求時」使用request對象,究竟是在哪裡使用呢?
  • Spring中獲取Request的幾種方法及其線程安全性分析
    由於在Spring MVC中,處理請求的Controller、Service等對象都是單例的,因此獲取request對象時最需要注意的問題,便是request對象是否是線程安全的:當有大量並發請求時,能否保證不同請求/線程中使用不同的request對象。這裡還有一個問題需要注意:前面所說的「在處理請求時」使用request對象,究竟是在哪裡使用呢?
  • Android設計模式(1)——單例模式
    DCL方式實現單例模式的優點點是既能夠在需要時才初始化實例,又能夠保證線程安全,且單例對象初始化調用getInstance不進行同步鎖。加上volatile會或多或少影響到程序的性能,但是為了保證程序的正確性,犧牲這點性能只值得的。DCL的優點:資源利用率高。缺點:第一次加載反應稍慢,也由於Java內存模型的原因會偶爾失敗,在高並發的環境下也有一定的缺陷。DCL模式是使用最多的單例實現方式,它在需要的時候才實例化單例,並且能在絕大多數場景下單例對象的唯一性。
  • 手寫單例模式
    單例的幾種實現1、懶漢模式(單線程使用)--懶漢式就是不在系統加載時就創建類的單例,而是在第一次使用實例的時候再創建。--多線程禁止使用。延遲加載(LazyLoading)很明顯嚴格來講不算單例模式,因為線程不安全,多線程下,可能產生多個對象。不是單例。
  • Swift中編寫單例的正確方式
    為保證單例的唯一性,單例類的初始化方法必須是私有的。這樣就可以避免其他對象通過單例類創建額外的實例。3. 考慮到規則1,為保證在整個程序的生命周期中值有一個實例被創建,單例必須是線程安全的。並發有時候確實挺複雜,簡單說來,如果單例的代碼不正確,如果有兩個線程同時實例化一個單例對象,就可能會創建出兩個單例對象。也就是說,必須保證單例的線程安全性,才可以保證其唯一性。
  • 單例模式襲來
    怎麼解決?看下面的惡漢式。2 餓漢式單例❝先創建一個放著,你要了直接拿走完事,應該叫"猴急式單例"。❝如果一個類裡面有"靜態賦值語句,靜態代碼塊",那麼這個類的class文件會生成一個<clinit>()方法來執行這些語句,這個方法會在類初始化的時候執行,並且"jvm會保證<clinit>()方法在多線程中被正確的加鎖、同步",因為jvm保證了<clinit>()是線程安全的,所以靜態賦值語句也是線程安全的,餓漢式單例中mInstance的創建就是靜態賦值語句
  • Java 實現單例模式的 9 種方法
    什麼是單例模式二. 單例模式的特點三. 單例模式VS靜態類四. 單例模式的實現一. 什麼是單例模式因進程需要,有時我們只需要某個類同時保留一個對象,不希望有更多對象,此時,我們則應考慮單例模式的設計。二. 單例模式的特點單例模式只能有一個實例。單例類必須創建自己的唯一實例。
  • 我向面試官講解了單例模式,他對我豎起了大拇指
    無論是完美的懶漢式還是餓漢式,終究敵不過反射和序列化,它們倆都可以把單例對象破壞掉(產生多個對象)。但是,追求極致的我們,怎麼能夠止步於此,在《Effective Java》書中,給出了終極解決方法,話不多說,學完下面,真的不虛面試官考你了。
  • 單例模式絕對沒有想像的那麼簡單!不服來看!
    一、前言單例模式(Singleton Pattern)是 Java 中最常用的設計模式之一,同時也是面試的重災區。有些人可能覺的單例模式很簡單,沒有什麼難的。其實不然,因為牽扯到線程安全的問題,所以單例模式絕對能體現出你的功底。不信接著往下看。
  • Java創建線程安全的單例singleton
    Spring 中的Bean,默認也是單例的,共享資源的訪問,比如日誌文件,系統配置單例的實現要實現一個單例,首先要把構造方法設置成私有的,並且要提供一個返回實例的方法1、實現一個單例,單線程是安全的,多線程中不安全,代碼如下:
  • Spring中獲取request的幾種方法,及線程安全性
    由於在Spring MVC中,處理請求的Controller、Service等對象都是單例的,因此獲取request對象時最需要注意的問題,便是request對象是否是線程安全的:當有大量並發請求時,能否保證不同請求/線程中使用不同的request對象。這裡還有一個問題需要注意:前面所說的「在處理請求時」使用request對象,究竟是在哪裡使用呢?
  • Java的單例模式
    一句話概括: 保證一個類有且僅有一個實例,並提供該實例的全局訪問方法所以為了達到單例的效果,一般要包含三個要素截取某博客的評論:雖然書裡也是說要加volatile關鍵字,但是還是和你有同樣的疑問。new Instance()確實非原子操作,但是synchronized不正是為了保證非原子操作的線程安全才用的麼?
  • Java單例模式深入詳解
    總之,選擇單例模式就是為了避免不一致狀態,避免政出多頭。正是由於這個特 點,單例對象通常作為程序中的存放配置信息的載體,因為它能保證其他對象讀到一致的信息。例如在某個伺服器程序中,該伺服器的配置信息可能存放在資料庫或 文件中,這些配置數據由某個單例對象統一讀取,服務進程中的其他對象如果要獲取這些配置信息,只需訪問該單例對象即可。
  • 一文帶你讀懂單例模式
    單例模式單例模式可以說是設計模式中最簡單的一個了,它屬於創建型模式,其定義如下:單例模式確保某個類只有一個實例,而且自行實例化並向整個系統提供這個實例。在應用這個模式時,單例對象的類必須保證只有一個實例存在。許多時候整個系統只需要擁有一個的全局對象,這樣有利於我們協調系統整體的行為。
  • 網友面試N多家公司,孟哥幫你總結面試題,再也不怕面試了
    @dependsOn@Configuration,@bean, @ComponentScan,@Aspect,@PointCut,@Scope,@PostConstruct,@PreDestorySpring的Bean默認是單例還是多例,我想改成多例的怎麼辦項目中用到了rabbitmq,使用中遇到什麼問題rabbitmq
  • 設計模式一:單例模式
    什麼是單例模式單例模式是指系統中的某個類只能有一個對象實例。
  • 深入淺出js單例模式
    何為單例模式?顧名思義,單例模式就是保證一個類僅有一個實例,也就是創建出來的兩個實例必須相等!