通過spring注解開發(fā),簡單測試單例和多例區(qū)別
通過spring注解開發(fā),測試單例和多例區(qū)別
1.注解和配置兩種用法形式
配置版:
注解版:
2.在spring框架中,scope作用域默認是單例的
注:以下測試均是注解版
3.實例
(1)多例:
配置類:
@Configuration public class PersonConfigure { //給容器中注冊一個bean,類型為返回值的類型,id為方法名 @Scope("prototype") //多例 @Bean() public Person person() { System.out.println("bean被加載到容器中"); return new Person("張三",23); } }
單元測試:
@Test public void test02() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(PersonConfigure.class); System.out.println("ioc容器加載完成"); Person bean = (Person) context.getBean("person"); bean.setName("lisi"); System.out.println(bean.toString()); Person bean1 = (Person) context.getBean("person"); System.out.println(bean1.toString()); System.out.println(bean==bean1); } }
測試結果:
結論:多例情況下,容器創(chuàng)建完成時不調用方法創(chuàng)建對象到容器中,在程序中獲取時,才會將對象加載到容器中,而且每次調用生成的都是不同的對象。
(2)單例(注解版)
配置類:
//默認是單例 @Configuration public class PersonConfigure { //給容器中注冊一個bean,類型為返回值的類型,id為方法名 @Bean() public Person person() { System.out.println("bean被加載到容器中"); return new Person("張三",23); } }
單元測試:
@Test public void test02() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(PersonConfigure.class); System.out.println("ioc容器加載完成"); Person bean = (Person) context.getBean("person"); bean.setName("lisi"); System.out.println(bean.toString()); Person bean1 = (Person) context.getBean("person"); System.out.println(bean1.toString()); System.out.println(bean==bean1); } }
測試結果:
結論:單例情況下,容器創(chuàng)建時調用方法創(chuàng)建對象到容器中,在程序中調用bean,直接從容器中拿取,且每次拿取的都是同一個對象。如果上一次對bean里的屬性做了修改,那下一次拿取的就是修改過的bean。
Spring中單例和多例的理解
1、什么是單例和多例
單例:所有請求用同一個對象來處理。通過單例模式,可以保證系統(tǒng)中一個類只有一個實例。
多例:每個請求用一個新的對象來處理。
2、Spring中的單例與多例
spring ioc容器的bean都是默認單例的,即spring依賴注入Bean實例默認是單例的。
spring提供了5中scope,分別是singleton,prototype,request,session,global session,常用是前兩種。點此查看官網(wǎng)介紹。
單例bean與多例(原型)bean的區(qū)別:
如果一個bean被聲明為單例的時候,在處理多次請求的時候,在spring容器里只實例化出一個bean,后續(xù)的請求都公用這個對象,這個對象會保存在一個map里面。當有請求來的時候,會先從緩存(map)里查看有沒有,有的話直接使用這個對象,沒有的話才實例化一個新的對象,所以這是個單例的。但是對于原型(prototype)bean來說,當每次請求來的時候,會直接實例化新的bean,沒有緩存以及緩存查詢的過程。
3、單例的優(yōu)勢與劣勢
優(yōu)勢:
由于不會創(chuàng)建新的對象,所以有以下幾個性能上的優(yōu)勢:
減少新生成實例的消耗。新生成實例包括兩個方面,第一,spring會通過反射或者cglib來生成bean實例,這都是耗性能的操作。第二,給對象分配內(nèi)存也會涉及負責算法。
減少jvm垃圾回收。由于不會給每個請求都生成bean實例,所以回收的對象就少了。
可以快速獲取到bean。因為單例獲取bean操作,除了第一次生成之外,其余都是從緩存里獲取的,所以很快。
劣勢:
一個很大的劣勢是它不能做到線程安全。由于所有請求都共享一個bean實例,那么如果這個bean是一個有狀態(tài)的bean的話,在并發(fā)場景下就有可能出現(xiàn)問題。
4、spring單例模式與線程安全:
當多用戶同時請求一個服務時,容器會給每一個請求分配一個線程,這時多個線程會并發(fā)執(zhí)行該請求所對應的業(yè)務邏輯(成員方法),此時就要注意了,如果該處理邏輯中有對該單例狀態(tài)的修改(體現(xiàn)為該單例的成員屬性),則必須考慮線程同步問題(此時該狀態(tài)就是一個臨界資源(共享數(shù)據(jù)),如果多個線程同時操作(修改)這個臨界資源就會誘發(fā)線程安全問題)。
線程安全:如果你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。如果每次運行的結果和單線程運行的結果是一樣的,而且其他的變量的值也和預期的是一樣的,就是線程安全的。或者說:一個類或者程序所提供的接口對于線程來說是原子操作,或者多線程之間的切換不會導致該接口的執(zhí)行結果存在二義性,就是線程安全的。
線程安全問題都是由全局變量及靜態(tài)變量引起的。
若每個線程中對全局變量,靜態(tài)變量只有讀操作,而無寫操作,一般來說,這個全局變量是線程安全的;若多個線程同時執(zhí)行寫操作,一般都需要考慮線程同步,否則就可能影響線程安全。
常量始終是線程安全的,因為只存在讀操作;
每次調用方法前都新建一個實例是線程安全的,因為不會訪問共享的資源;
局部變量是線程安全的。因為每執(zhí)行一個方法,都會在獨立的空間創(chuàng)建局部變量,它不是共享資源。局部變量包括方法的參數(shù)變量和方法內(nèi)的變量。
在關于spring單例與線程安全的很多文章中,會提到一個概念,即有狀態(tài)bean和無狀態(tài)bean。
- 無狀態(tài)bean:無狀態(tài),就是一次操作,不能保存數(shù)據(jù)。無狀態(tài)bean,就是沒有實例變量的對象,不能保存數(shù)據(jù),是不變類,在線程安全的。
- 有狀態(tài)bean:有狀態(tài),就是有數(shù)據(jù)存儲功能。有狀態(tài)bean,就是有實例變量的對象,可以保存數(shù)據(jù),是非線程安全的。
如何解決線程安全問題?
(1)使用線程同步機制:通過對象的鎖機制保證同一時間只有一個線程訪問變量。這時該變量是多個線程共享的,使用同步機制要求程序縝密地分析什么時候對變量進行讀寫,什么時候需要鎖定某個對象,什么時候釋放對象鎖等繁雜問題,程序設計和編寫難度相對較大。
(2)使用ThreadLocal:為每一個線程提供一個獨立的變量副本,從而隔離了多個線程對數(shù)據(jù)的訪問沖突。因為每一個線程都擁有自己的變量副本,從而也就沒有必要對該變量進行同步了。
概括起來就是:對于多線程資源共享的問題,同步機制采用了“以時間換空間”的方式,而ThreadLocal采用了“以空間換時間”的方式。前者僅提供一份變量,讓不同的線程排隊訪問,而后者為每一個線程都提供了一份變量,因此可以同時訪問而互不影響。
5、單例如何變多例
Scope聲明為prototype,即
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
在?Spring?Boot?中使用?Quartz?調度作業(yè)的示例詳解
這篇文章主要介紹了在?Spring?Boot?中使用?Quartz?調度作業(yè)的示例詳解,在本文中,我們將看看如何使用Quartz框架來調度任務,Quartz支持在特定時間運行作業(yè)、重復作業(yè)執(zhí)行、將作業(yè)存儲在數(shù)據(jù)庫中以及Spring集成,需要的朋友可以參考下2022-07-07SpringBoot @ExceptionHandler與@ControllerAdvice異常處理詳解
在Spring Boot應用的開發(fā)中,不管是對底層數(shù)據(jù)庫操作,對業(yè)務層操作,還是對控制層操作,都會不可避免的遇到各種可預知的,不可預知的異常需要處理,如果每個處理過程都單獨處理異常,那么系統(tǒng)的代碼耦合度會很高,工作量大且不好統(tǒng)一,以后維護的工作量也很大2022-10-10IDEA設置生成帶注釋的getter和setter的圖文教程
通常我們用idea默認生成的getter和setter方法是不帶注釋的,當然,我們同樣可以設置idea像MyEclipse一樣生成帶有Javadoc的模板,具體設置方法,大家參考下本文2018-05-05