通過(guò)spring注解開(kāi)發(fā),簡(jiǎn)單測(cè)試單例和多例區(qū)別
通過(guò)spring注解開(kāi)發(fā),測(cè)試單例和多例區(qū)別
1.注解和配置兩種用法形式
配置版:

注解版:

2.在spring框架中,scope作用域默認(rèn)是單例的
注:以下測(cè)試均是注解版
3.實(shí)例
(1)多例:
配置類(lèi):
@Configuration
public class PersonConfigure {
//給容器中注冊(cè)一個(gè)bean,類(lèi)型為返回值的類(lèi)型,id為方法名
@Scope("prototype") //多例
@Bean()
public Person person() {
System.out.println("bean被加載到容器中");
return new Person("張三",23);
}
}
單元測(cè)試:
@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);
}
}
測(cè)試結(jié)果:

結(jié)論:多例情況下,容器創(chuàng)建完成時(shí)不調(diào)用方法創(chuàng)建對(duì)象到容器中,在程序中獲取時(shí),才會(huì)將對(duì)象加載到容器中,而且每次調(diào)用生成的都是不同的對(duì)象。
(2)單例(注解版)
配置類(lèi):
//默認(rèn)是單例
@Configuration
public class PersonConfigure {
//給容器中注冊(cè)一個(gè)bean,類(lèi)型為返回值的類(lèi)型,id為方法名
@Bean()
public Person person() {
System.out.println("bean被加載到容器中");
return new Person("張三",23);
}
}
單元測(cè)試:
@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);
}
}
測(cè)試結(jié)果:

結(jié)論:?jiǎn)卫闆r下,容器創(chuàng)建時(shí)調(diào)用方法創(chuàng)建對(duì)象到容器中,在程序中調(diào)用bean,直接從容器中拿取,且每次拿取的都是同一個(gè)對(duì)象。如果上一次對(duì)bean里的屬性做了修改,那下一次拿取的就是修改過(guò)的bean。
Spring中單例和多例的理解
1、什么是單例和多例
單例:所有請(qǐng)求用同一個(gè)對(duì)象來(lái)處理。通過(guò)單例模式,可以保證系統(tǒng)中一個(gè)類(lèi)只有一個(gè)實(shí)例。
多例:每個(gè)請(qǐng)求用一個(gè)新的對(duì)象來(lái)處理。
2、Spring中的單例與多例
spring ioc容器的bean都是默認(rèn)單例的,即spring依賴(lài)注入Bean實(shí)例默認(rèn)是單例的。
spring提供了5中scope,分別是singleton,prototype,request,session,global session,常用是前兩種。點(diǎn)此查看官網(wǎng)介紹。
單例bean與多例(原型)bean的區(qū)別:
如果一個(gè)bean被聲明為單例的時(shí)候,在處理多次請(qǐng)求的時(shí)候,在spring容器里只實(shí)例化出一個(gè)bean,后續(xù)的請(qǐng)求都公用這個(gè)對(duì)象,這個(gè)對(duì)象會(huì)保存在一個(gè)map里面。當(dāng)有請(qǐng)求來(lái)的時(shí)候,會(huì)先從緩存(map)里查看有沒(méi)有,有的話直接使用這個(gè)對(duì)象,沒(méi)有的話才實(shí)例化一個(gè)新的對(duì)象,所以這是個(gè)單例的。但是對(duì)于原型(prototype)bean來(lái)說(shuō),當(dāng)每次請(qǐng)求來(lái)的時(shí)候,會(huì)直接實(shí)例化新的bean,沒(méi)有緩存以及緩存查詢的過(guò)程。
3、單例的優(yōu)勢(shì)與劣勢(shì)
優(yōu)勢(shì):
由于不會(huì)創(chuàng)建新的對(duì)象,所以有以下幾個(gè)性能上的優(yōu)勢(shì):
減少新生成實(shí)例的消耗。新生成實(shí)例包括兩個(gè)方面,第一,spring會(huì)通過(guò)反射或者cglib來(lái)生成bean實(shí)例,這都是耗性能的操作。第二,給對(duì)象分配內(nèi)存也會(huì)涉及負(fù)責(zé)算法。
減少jvm垃圾回收。由于不會(huì)給每個(gè)請(qǐng)求都生成bean實(shí)例,所以回收的對(duì)象就少了。
可以快速獲取到bean。因?yàn)閱卫@取bean操作,除了第一次生成之外,其余都是從緩存里獲取的,所以很快。
劣勢(shì):
一個(gè)很大的劣勢(shì)是它不能做到線程安全。由于所有請(qǐng)求都共享一個(gè)bean實(shí)例,那么如果這個(gè)bean是一個(gè)有狀態(tài)的bean的話,在并發(fā)場(chǎng)景下就有可能出現(xiàn)問(wèn)題。
4、spring單例模式與線程安全:
當(dāng)多用戶同時(shí)請(qǐng)求一個(gè)服務(wù)時(shí),容器會(huì)給每一個(gè)請(qǐng)求分配一個(gè)線程,這時(shí)多個(gè)線程會(huì)并發(fā)執(zhí)行該請(qǐng)求所對(duì)應(yīng)的業(yè)務(wù)邏輯(成員方法),此時(shí)就要注意了,如果該處理邏輯中有對(duì)該單例狀態(tài)的修改(體現(xiàn)為該單例的成員屬性),則必須考慮線程同步問(wèn)題(此時(shí)該狀態(tài)就是一個(gè)臨界資源(共享數(shù)據(jù)),如果多個(gè)線程同時(shí)操作(修改)這個(gè)臨界資源就會(huì)誘發(fā)線程安全問(wèn)題)。
線程安全:如果你的代碼所在的進(jìn)程中有多個(gè)線程在同時(shí)運(yùn)行,而這些線程可能會(huì)同時(shí)運(yùn)行這段代碼。如果每次運(yùn)行的結(jié)果和單線程運(yùn)行的結(jié)果是一樣的,而且其他的變量的值也和預(yù)期的是一樣的,就是線程安全的?;蛘哒f(shuō):一個(gè)類(lèi)或者程序所提供的接口對(duì)于線程來(lái)說(shuō)是原子操作,或者多線程之間的切換不會(huì)導(dǎo)致該接口的執(zhí)行結(jié)果存在二義性,就是線程安全的。
線程安全問(wèn)題都是由全局變量及靜態(tài)變量引起的。
若每個(gè)線程中對(duì)全局變量,靜態(tài)變量只有讀操作,而無(wú)寫(xiě)操作,一般來(lái)說(shuō),這個(gè)全局變量是線程安全的;若多個(gè)線程同時(shí)執(zhí)行寫(xiě)操作,一般都需要考慮線程同步,否則就可能影響線程安全。
常量始終是線程安全的,因?yàn)橹淮嬖谧x操作;
每次調(diào)用方法前都新建一個(gè)實(shí)例是線程安全的,因?yàn)椴粫?huì)訪問(wèn)共享的資源;
局部變量是線程安全的。因?yàn)槊繄?zhí)行一個(gè)方法,都會(huì)在獨(dú)立的空間創(chuàng)建局部變量,它不是共享資源。局部變量包括方法的參數(shù)變量和方法內(nèi)的變量。
在關(guān)于spring單例與線程安全的很多文章中,會(huì)提到一個(gè)概念,即有狀態(tài)bean和無(wú)狀態(tài)bean。
- 無(wú)狀態(tài)bean:無(wú)狀態(tài),就是一次操作,不能保存數(shù)據(jù)。無(wú)狀態(tài)bean,就是沒(méi)有實(shí)例變量的對(duì)象,不能保存數(shù)據(jù),是不變類(lèi),在線程安全的。
- 有狀態(tài)bean:有狀態(tài),就是有數(shù)據(jù)存儲(chǔ)功能。有狀態(tài)bean,就是有實(shí)例變量的對(duì)象,可以保存數(shù)據(jù),是非線程安全的。
如何解決線程安全問(wèn)題?
(1)使用線程同步機(jī)制:通過(guò)對(duì)象的鎖機(jī)制保證同一時(shí)間只有一個(gè)線程訪問(wèn)變量。這時(shí)該變量是多個(gè)線程共享的,使用同步機(jī)制要求程序縝密地分析什么時(shí)候?qū)ψ兞窟M(jìn)行讀寫(xiě),什么時(shí)候需要鎖定某個(gè)對(duì)象,什么時(shí)候釋放對(duì)象鎖等繁雜問(wèn)題,程序設(shè)計(jì)和編寫(xiě)難度相對(duì)較大。
(2)使用ThreadLocal:為每一個(gè)線程提供一個(gè)獨(dú)立的變量副本,從而隔離了多個(gè)線程對(duì)數(shù)據(jù)的訪問(wèn)沖突。因?yàn)槊恳粋€(gè)線程都擁有自己的變量副本,從而也就沒(méi)有必要對(duì)該變量進(jìn)行同步了。
概括起來(lái)就是:對(duì)于多線程資源共享的問(wèn)題,同步機(jī)制采用了“以時(shí)間換空間”的方式,而ThreadLocal采用了“以空間換時(shí)間”的方式。前者僅提供一份變量,讓不同的線程排隊(duì)訪問(wèn),而后者為每一個(gè)線程都提供了一份變量,因此可以同時(shí)訪問(wèn)而互不影響。
5、單例如何變多例
Scope聲明為prototype,即
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
mybatis調(diào)用存儲(chǔ)過(guò)程的實(shí)例代碼
這篇文章主要介紹了mybatis調(diào)用存儲(chǔ)過(guò)程的實(shí)例,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-10-10
Java正則表達(dá)式_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
什么是正則表達(dá)式,正則表達(dá)式的作用是什么?這篇文章主要為大家詳細(xì)介紹了Java正則表達(dá)式的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05
如何配置cursor進(jìn)行Java springboot項(xiàng)目開(kāi)發(fā)
本文介紹了如何在Cursor IDE中配置Java和Spring Boot項(xiàng)目開(kāi)發(fā)環(huán)境,首先,設(shè)置了系統(tǒng)用戶級(jí)別的JDK配置,以便在多個(gè)項(xiàng)目之間切換時(shí)不需要重新配置,然后,配置了Gradle環(huán)境變量,并安裝了必要的Java開(kāi)發(fā)插件,感興趣的朋友跟隨小編一起看看2025-02-02
mybatis主表與明細(xì)表一對(duì)多的同時(shí)插入操作方法
對(duì)主表(采購(gòu)申請(qǐng)表)和明細(xì)表(申請(qǐng)物資表)同時(shí)進(jìn)行插入操作insert,怎么實(shí)現(xiàn)呢,下面給大家分享mybatis主表與明細(xì)表一對(duì)多的同時(shí)插入操作方法,感興趣的朋友一起看看吧2023-02-02
淺談Arrays.asList() 和ArrayList類(lèi)型區(qū)別
下面小編就為大家?guī)?lái)一篇Arrays.asList() 和ArrayList類(lèi)型區(qū)別。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-10-10
ElasticSearch啟動(dòng)成功卻無(wú)法在瀏覽器訪問(wèn)問(wèn)題解決辦法
因工作的需要,要使用elasticsearch,安裝完了,啟動(dòng)也成功了之后發(fā)現(xiàn)了問(wèn)題,這篇文章主要給大家介紹了關(guān)于ElasticSearch啟動(dòng)成功卻無(wú)法在瀏覽器訪問(wèn)問(wèn)題的解決辦法,需要的朋友可以參考下2024-10-10
SpringBoot集成ElasticSearch(ES)實(shí)現(xiàn)全文搜索功能
Elasticsearch是一個(gè)開(kāi)源的分布式搜索和分析引擎,它被設(shè)計(jì)用于處理大規(guī)模數(shù)據(jù)集,它提供了一個(gè)分布式多用戶能力的全文搜索引擎,本文將給大家介紹SpringBoot集成ElasticSearch(ES)實(shí)現(xiàn)全文搜索功能,需要的朋友可以參考下2024-02-02

