日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

spring 单例 获取多例的位_Spring系列第6篇:玩转bean scope,避免跳坑里!

發(fā)布時(shí)間:2023/12/4 javascript 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 spring 单例 获取多例的位_Spring系列第6篇:玩转bean scope,避免跳坑里! 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

公眾號(hào)關(guān)注“程序員二哥”,

設(shè)為‘星標(biāo)’,帶你學(xué)習(xí)更多的知識(shí)。

本文內(nèi)容

  • 詳細(xì)介紹5中bean的sope及使用注意點(diǎn)

  • 自定義作用域的實(shí)現(xiàn)

  • 應(yīng)用中,有時(shí)候我們需要一個(gè)對(duì)象在整個(gè)應(yīng)用中只有一個(gè),有些對(duì)象希望每次使用的時(shí)候都重新創(chuàng)建一個(gè),spring對(duì)我們這種需求也提供了支持,在spring中這個(gè)叫做bean的作用域,xml中定義bean的時(shí)候,可以通過scope屬性指定bean的作用域,如:

    <bean?id=""?class=""?scope="作用域"?/>?

    spring容器中scope常見的有5種,下面我們分別來介紹一下。

    singleton

    當(dāng)scope的值設(shè)置為singleton的時(shí)候,整個(gè)spring容器中只會(huì)存在一個(gè)bean實(shí)例,通過容器多次查找bean的時(shí)候(調(diào)用BeanFactory的getBean方法或者bean之間注入依賴的bean對(duì)象的時(shí)候),返回的都是同一個(gè)bean對(duì)象,singleton是scope的默認(rèn)值,所以spring容器中默認(rèn)創(chuàng)建的bean對(duì)象是單例的,通常spring容器在啟動(dòng)的時(shí)候,會(huì)將scope為singleton的bean創(chuàng)建好放在容器中(有個(gè)特殊的情況,當(dāng)bean的lazy被設(shè)置為true的時(shí)候,表示懶加載,那么使用的時(shí)候才會(huì)創(chuàng)建),用的時(shí)候直接返回。

    案例

    bean xml配置

    <bean?id="singletonBean"?class="com.javacode2018.lesson001.demo4.BeanScopeModel"?scope="singleton">
    ????<constructor-arg?index="0"?value="singleton"/>
    bean>
    BeanScopeModel代碼
    package?com.javacode2018.lesson001.demo4;

    public?class?BeanScopeModel?{
    ????public?BeanScopeModel(String?beanScope)?{
    ????????System.out.println(String.format("create?BeanScopeModel,{sope=%s},{this=%s}",?beanScope,?this));
    ????}
    }

    上面構(gòu)造方法中輸出了一段文字,一會(huì)我們可以根據(jù)輸出來看一下這個(gè)bean什么時(shí)候創(chuàng)建的,是從容器中獲取bean的時(shí)候創(chuàng)建的還是容器啟動(dòng)的時(shí)候創(chuàng)建的。

    測試用例
    package?com.javacode2018.lesson001.demo4;

    import?org.junit.Before;
    import?org.junit.Test;
    import?org.springframework.context.support.ClassPathXmlApplicationContext;

    /**
    ?*?公眾號(hào):路人甲Java,工作10年的前阿里P7分享Java、算法、數(shù)據(jù)庫方面的技術(shù)干貨!堅(jiān)信用技術(shù)改變命運(yùn),讓家人過上更體面的生活!
    ?*?


    ?*?bean作用域
    ?*/


    public?class?ScopeTest?{

    ????ClassPathXmlApplicationContext?context;

    ????@Before
    ????public?void?before()?{
    ????????System.out.println("spring容器準(zhǔn)備啟動(dòng).....");
    ????????//1.bean配置文件位置
    ????????String?beanXml?=?"classpath:/com/javacode2018/lesson001/demo4/beans.xml";
    ????????//2.創(chuàng)建ClassPathXmlApplicationContext容器,給容器指定需要加載的bean配置文件
    ????????this.context?=?new?ClassPathXmlApplicationContext(beanXml);
    ????????System.out.println("spring容器啟動(dòng)完畢!");
    ????}

    ????/**
    ?????*?單例bean
    ?????*/
    ????@Test
    ????public?void?singletonBean()?{
    ????????System.out.println("---------單例bean,每次獲取的bean實(shí)例都一樣---------");
    ????????System.out.println(context.getBean("singletonBean"));
    ????????System.out.println(context.getBean("singletonBean"));
    ????????System.out.println(context.getBean("singletonBean"));
    ????}

    }

    上面代碼中before方法上面有@Before注解,這個(gè)是junit提供的功能,這個(gè)方法會(huì)在所有@Test標(biāo)注的方法之前之前運(yùn)行,before方法中我們對(duì)容器進(jìn)行初始化,并且在容器初始化前后輸出了一段文字。

    上面代碼中,singletonBean方法中,3次獲取singletonBean對(duì)應(yīng)的bean。

    運(yùn)行測試用例
    spring容器準(zhǔn)備啟動(dòng).....
    create?BeanScopeModel,{sope=singleton},{this=com.javacode2018.lesson001.demo4.BeanScopeModel@e874448}
    spring容器啟動(dòng)完畢!
    ---------單例bean,每次獲取的bean實(shí)例都一樣---------
    com.javacode2018.lesson001.demo4.BeanScopeModel@e874448
    com.javacode2018.lesson001.demo4.BeanScopeModel@e874448
    com.javacode2018.lesson001.demo4.BeanScopeModel@e874448
    結(jié)論

    從輸出中得到2個(gè)結(jié)論

    • 前3行的輸出可以看出,BeanScopeModel的構(gòu)造方法是在容器啟動(dòng)過程中調(diào)用的,說明這個(gè)bean實(shí)例在容器啟動(dòng)過程中就創(chuàng)建好了,放在容器中緩存著

    • 最后3行輸出的是一樣的,說明返回的是同一個(gè)bean對(duì)象

    單例bean使用注意

    單例bean是整個(gè)應(yīng)用共享的,所以需要考慮到線程安全問題,之前在玩springmvc的時(shí)候,springmvc中controller默認(rèn)是單例的,有些開發(fā)者在controller中創(chuàng)建了一些變量,那么這些變量實(shí)際上就變成共享的了,controller可能會(huì)被很多線程同時(shí)訪問,這些線程并發(fā)去修改controller中的共享變量,可能會(huì)出現(xiàn)數(shù)據(jù)錯(cuò)亂的問題;所以使用的時(shí)候需要特別注意。

    prototype

    如果scope被設(shè)置為prototype類型的了,表示這個(gè)bean是多例的,通過容器每次獲取的bean都是不同的實(shí)例,每次獲取都會(huì)重新創(chuàng)建一個(gè)bean實(shí)例對(duì)象。

    案例

    bean xml配置

    <bean?id="prototypeBean"?class="com.javacode2018.lesson001.demo4.BeanScopeModel"?scope="prototype">
    ????<constructor-arg?index="0"?value="prototype"/>
    bean>
    新增一個(gè)測試用例

    ScopeTest中新增一個(gè)方法

    /**
    ?*?多例bean
    ?*/
    @Test
    public?void?prototypeBean()?{
    ????System.out.println("---------單例bean,每次獲取的bean實(shí)例都一樣---------");
    ????System.out.println(context.getBean("prototypeBean"));
    ????System.out.println(context.getBean("prototypeBean"));
    ????System.out.println(context.getBean("prototypeBean"));
    }
    運(yùn)行測試用例
    spring容器準(zhǔn)備啟動(dòng).....
    spring容器啟動(dòng)完畢!
    ---------單例bean,每次獲取的bean實(shí)例都一樣---------
    create?BeanScopeModel,{sope=prototype},{this=com.javacode2018.lesson001.demo4.BeanScopeModel@289d1c02}
    com.javacode2018.lesson001.demo4.BeanScopeModel@289d1c02
    create?BeanScopeModel,{sope=prototype},{this=com.javacode2018.lesson001.demo4.BeanScopeModel@22eeefeb}
    com.javacode2018.lesson001.demo4.BeanScopeModel@22eeefeb
    create?BeanScopeModel,{sope=prototype},{this=com.javacode2018.lesson001.demo4.BeanScopeModel@17d0685f}
    com.javacode2018.lesson001.demo4.BeanScopeModel@17d0685f
    結(jié)論

    輸出中可以看出,容器啟動(dòng)過程中并沒有去創(chuàng)建BeanScopeModel對(duì)象,3次獲取prototypeBean得到的都是不同的實(shí)例,每次獲取的時(shí)候才會(huì)去調(diào)用構(gòu)造方法創(chuàng)建bean實(shí)例。

    多例bean使用注意

    多例bean每次獲取的時(shí)候都會(huì)重新創(chuàng)建,如果這個(gè)bean比較復(fù)雜,創(chuàng)建時(shí)間比較長,會(huì)影響系統(tǒng)的性能,這個(gè)地方需要注意。

    下面要介紹的3個(gè):request、session、application都是在spring web容器環(huán)境中才會(huì)有的。

    request

    當(dāng)一個(gè)bean的作用域?yàn)閞equest,表示在一次http請(qǐng)求中,一個(gè)bean對(duì)應(yīng)一個(gè)實(shí)例;對(duì)每個(gè)http請(qǐng)求都會(huì)創(chuàng)建一個(gè)bean實(shí)例,request結(jié)束的時(shí)候,這個(gè)bean也就結(jié)束了,request作用域用在spring容器的web環(huán)境中,這個(gè)以后講springmvc的時(shí)候會(huì)說,spring中有個(gè)web容器接口WebApplicationContext,這個(gè)里面對(duì)request作用域提供了支持,配置方式:

    <bean?id=""?class=""?scope="request"?/>

    session

    這個(gè)和request類似,也是用在web環(huán)境中,session級(jí)別共享的bean,每個(gè)會(huì)話會(huì)對(duì)應(yīng)一個(gè)bean實(shí)例,不同的session對(duì)應(yīng)不同的bean實(shí)例,springmvc中我們?cè)偌?xì)說。

    <bean?id=""?class=""?scope="session"?/>

    application

    全局web應(yīng)用級(jí)別的作用于,也是在web環(huán)境中使用的,一個(gè)web應(yīng)用程序?qū)?yīng)一個(gè)bean實(shí)例,通常情況下和singleton效果類似的,不過也有不一樣的地方,singleton是每個(gè)spring容器中只有一個(gè)bean實(shí)例,一般我們的程序只有一個(gè)spring容器,但是,一個(gè)應(yīng)用程序中可以創(chuàng)建多個(gè)spring容器,不同的容器中可以存在同名的bean,但是sope=aplication的時(shí)候,不管應(yīng)用中有多少個(gè)spring容器,這個(gè)應(yīng)用中同名的bean只有一個(gè)。

    <bean?id=""?class=""?scope="application"?/>

    自定義scope

    有時(shí)候,spring內(nèi)置的幾種sope都無法滿足我們的需求的時(shí)候,我們可以自定義bean的作用域。

    自定義Scope 3步驟

    第1步:實(shí)現(xiàn)Scope接口

    我們來看一下這個(gè)接口定義

    package?org.springframework.beans.factory.config;

    import?org.springframework.beans.factory.ObjectFactory;
    import?org.springframework.lang.Nullable;

    public?interface?Scope?{

    ????/**
    ????*?返回當(dāng)前作用域中name對(duì)應(yīng)的bean對(duì)象
    ????* name:需要檢索的bean的名稱
    ????* objectFactory:如果name對(duì)應(yīng)的bean在當(dāng)前作用域中沒有找到,那么可以調(diào)用這個(gè)ObjectFactory來創(chuàng)建這個(gè)對(duì)象
    ????**/
    ????Object?get(String?name,?ObjectFactory>?objectFactory);

    ????/**
    ?????*?將name對(duì)應(yīng)的bean從當(dāng)前作用域中移除
    ?????**/
    ????@Nullable
    ????Object?remove(String?name);

    ????/**
    ?????*?用于注冊(cè)銷毀回調(diào),如果想要銷毀相應(yīng)的對(duì)象,則由Spring容器注冊(cè)相應(yīng)的銷毀回調(diào),而由自定義作用域選擇是不是要銷毀相應(yīng)的對(duì)象
    ?????*/
    ????void?registerDestructionCallback(String?name,?Runnable?callback);

    ????/**
    ?????*?用于解析相應(yīng)的上下文數(shù)據(jù),比如request作用域?qū)⒎祷豶equest中的屬性。
    ?????*/
    ????@Nullable
    ????Object?resolveContextualObject(String?key);

    ????/**
    ?????*?作用域的會(huì)話標(biāo)識(shí),比如session作用域?qū)⑹莝essionId
    ?????*/
    ????@Nullable
    ????String?getConversationId();

    }
    第2步:將自定義的scope注冊(cè)到容器

    需要調(diào)用org.springframework.beans.factory.config.ConfigurableBeanFactory#registerScope的方法,看一下這個(gè)方法的聲明

    /**
    *?向容器中注冊(cè)自定義的Scope
    *scopeName:作用域名稱
    * scope:作用域?qū)ο?br />**/
    void?registerScope(String?scopeName,?Scope?scope);
    第3步:使用自定義的作用域

    定義bean的時(shí)候,指定bean的scope屬性為自定義的作用域名稱。

    案例

    需求

    下面我們來實(shí)現(xiàn)一個(gè)線程級(jí)別的bean作用域,同一個(gè)線程中同名的bean是同一個(gè)實(shí)例,不同的線程中的bean是不同的實(shí)例。

    實(shí)現(xiàn)分析

    需求中要求bean在線程中是貢獻(xiàn)的,所以我們可以通過ThreadLocal來實(shí)現(xiàn),ThreadLocal可以實(shí)現(xiàn)線程中數(shù)據(jù)的共享。

    下面我們來上代碼。

    ThreadScope
    package?com.javacode2018.lesson001.demo4;

    import?org.springframework.beans.factory.ObjectFactory;
    import?org.springframework.beans.factory.config.Scope;
    import?org.springframework.lang.Nullable;

    import?java.util.HashMap;
    import?java.util.Map;
    import?java.util.Objects;

    /**
    ?*?自定義本地線程級(jí)別的bean作用域,不同的線程中對(duì)應(yīng)的bean實(shí)例是不同的,同一個(gè)線程中同名的bean是同一個(gè)實(shí)例
    ?*/
    public?class?ThreadScope?implements?Scope?{

    ????public?static?final?String?THREAD_SCOPE?=?"thread";//@1

    ????private?ThreadLocal>?beanMap?=?new?ThreadLocal()?{@Overrideprotected?Object?initialValue()?{return?new?HashMap<>();
    ????????}
    ????};@Overridepublic?Object?get(String?name,?ObjectFactory>?objectFactory)?{
    ????????Object?bean?=?beanMap.get().get(name);if?(Objects.isNull(bean))?{
    ????????????bean?=?objectFactory.getObject();
    ????????????beanMap.get().put(name,?bean);
    ????????}return?bean;
    ????}@Nullable@Overridepublic?Object?remove(String?name)?{return?this.beanMap.get().remove(name);
    ????}@Overridepublic?void?registerDestructionCallback(String?name,?Runnable?callback)?{//bean作用域范圍結(jié)束的時(shí)候調(diào)用的方法,用于bean清理
    ????????System.out.println(name);
    ????}@Nullable@Overridepublic?Object?resolveContextualObject(String?key)?{return?null;
    ????}@Nullable@Overridepublic?String?getConversationId()?{return?Thread.currentThread().getName();
    ????}
    }

    @1:定義了作用域的名稱為一個(gè)常量thread,可以在定義bean的時(shí)候給scope使用

    BeanScopeModel
    package?com.javacode2018.lesson001.demo4;

    public?class?BeanScopeModel?{
    ????public?BeanScopeModel(String?beanScope)?{
    ????????System.out.println(String.format("線程:%s,create?BeanScopeModel,{sope=%s},{this=%s}",?Thread.currentThread(),?beanScope,?this));
    ????}
    }

    上面的構(gòu)造方法中會(huì)輸出當(dāng)前線程的信息,到時(shí)候可以看到創(chuàng)建bean的線程。

    bean配置文件

    beans-thread.xml內(nèi)容

    <?xml ?version="1.0"?encoding="UTF-8"?>
    <beans?xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans
    ????http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">

    ????
    ????<bean?id="threadBean"?class="com.javacode2018.lesson001.demo4.BeanScopeModel"?scope="thread">
    ????????<constructor-arg?index="0"?value="thread"/>
    ????bean>
    beans>

    注意上面的scope是我們自定義的,值為thread

    測試用例
    package?com.javacode2018.lesson001.demo4;

    import?org.springframework.context.support.ClassPathXmlApplicationContext;

    import?java.util.concurrent.TimeUnit;

    /**
    ?*?公眾號(hào):路人甲Java,工作10年的前阿里P7分享Java、算法、數(shù)據(jù)庫方面的技術(shù)干貨!堅(jiān)信用技術(shù)改變命運(yùn),讓家人過上更體面的生活!
    ?*?


    ?*?自定義scope
    ?*/


    public?class?ThreadScopeTest?{
    ????public?static?void?main(String[]?args)?throws?InterruptedException?{
    ????????String?beanXml?=?"classpath:/com/javacode2018/lesson001/demo4/beans-thread.xml";
    ????????//手動(dòng)創(chuàng)建容器
    ????????ClassPathXmlApplicationContext?context?=?new?ClassPathXmlApplicationContext();
    ????????//設(shè)置配置文件位置
    ????????context.setConfigLocation(beanXml);
    ????????//啟動(dòng)容器
    ????????context.refresh();
    ????????//向容器中注冊(cè)自定義的scope
    ????????context.getBeanFactory().registerScope(ThreadScope.THREAD_SCOPE,?new?ThreadScope());//@1

    ????????//使用容器獲取bean
    ????????for?(int?i?=?0;?i?2;?i++)?{?//@2
    ????????????new?Thread(()?->?{
    ????????????????System.out.println(Thread.currentThread()?+?","?+?context.getBean("threadBean"));
    ????????????????System.out.println(Thread.currentThread()?+?","?+?context.getBean("threadBean"));
    ????????????}).start();
    ????????????TimeUnit.SECONDS.sleep(1);
    ????????}
    ????}
    }

    注意上面代碼,重點(diǎn)在@1,這個(gè)地方向容器中注冊(cè)了自定義的ThreadScope。

    @2:創(chuàng)建了2個(gè)線程,然后在每個(gè)線程中去獲取同樣的bean 2次,然后輸出,我們來看一下效果。

    運(yùn)行輸出
    線程:Thread[Thread-1,5,main],create?BeanScopeModel,{sope=thread},{this=com.javacode2018.lesson001.demo4.BeanScopeModel@4049d530}
    Thread[Thread-1,5,main],com.javacode2018.lesson001.demo4.BeanScopeModel@4049d530
    Thread[Thread-1,5,main],com.javacode2018.lesson001.demo4.BeanScopeModel@4049d530
    線程:Thread[Thread-2,5,main],create?BeanScopeModel,{sope=thread},{this=com.javacode2018.lesson001.demo4.BeanScopeModel@87a76da}
    Thread[Thread-2,5,main],com.javacode2018.lesson001.demo4.BeanScopeModel@87a76da
    Thread[Thread-2,5,main],com.javacode2018.lesson001.demo4.BeanScopeModel@87a76da

    從輸出中可以看到,bean在同樣的線程中獲取到的是同一個(gè)bean的實(shí)例,不同的線程中bean的實(shí)例是不同的。

    總結(jié)

  • spring容器自帶的有2種作用域,分別是singleton和prototype;還有3種分別是spring web容器環(huán)境中才支持的request、session、application

  • singleton是spring容器默認(rèn)的作用域,一個(gè)spring容器中同名的bean實(shí)例只有一個(gè),多次獲取得到的是同一個(gè)bean;單例的bean需要考慮線程安全問題

  • prototype是多例的,每次從容器中獲取同名的bean,都會(huì)重新創(chuàng)建一個(gè);多例bean使用的時(shí)候需要考慮創(chuàng)建bean對(duì)性能的影響

  • 一個(gè)應(yīng)用中可以有多個(gè)spring容器

  • 自定義scope 3個(gè)步驟,實(shí)現(xiàn)Scope接口,將實(shí)現(xiàn)類注冊(cè)到spring容器,使用自定義的sope

  • 案例源碼

    鏈接:https://pan.baidu.com/s/1p6rcfKOeWQIVkuhVybzZmQ?
    提取碼:zr99

    Spring系列

  • Spring系列第1篇:為何要學(xué)spring?

  • Spring系列第2篇:控制反轉(zhuǎn)(IoC)與依賴注入(DI)

  • Spring系列第3篇:Spring容器基本使用及原理

  • Spring系列第4篇:xml中bean定義詳解(-)

  • Spring系列第5篇:創(chuàng)建bean實(shí)例這些方式你們都知道?

  • 更多好文章

  • Java高并發(fā)系列(共34篇)

  • MySql高手系列(共27篇)

  • Maven高手系列(共10篇)

  • Mybatis系列(共12篇)

  • 聊聊db和緩存一致性常見的實(shí)現(xiàn)方式

  • 接口冪等性這么重要,它是什么?怎么實(shí)現(xiàn)?

  • 泛型,有點(diǎn)難度,會(huì)讓很多人懵逼,那是因?yàn)槟銢]有看這篇文章!

  • 感謝大家的閱讀,也歡迎您把這篇文章分享給更多的朋友一起閱讀!謝謝!

    ﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌

    如果覺得文章還不錯(cuò),

    大家可以掃碼點(diǎn)個(gè)關(guān)注,

    和你一起成長,學(xué)習(xí)更多知識(shí)。

    總結(jié)

    以上是生活随笔為你收集整理的spring 单例 获取多例的位_Spring系列第6篇:玩转bean scope,避免跳坑里!的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。