亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

解決@Scope(“prototype“)不生效的問題

 更新時間:2022年06月15日 08:34:37   作者:夢想blog  
這篇文章主要介紹了解決@Scope(“prototype“)不生效的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

@Scope(“prototype“)不生效

使用spring的時候,我們一般都是使用@Component實現(xiàn)bean的注入,這個時候我們的bean如果不指定@Scope,默認是單例模式,另外還有很多模式可用,用的最多的就是多例模式了,顧名思義就是每次使用都會創(chuàng)建一個新的對象,比較適用于寫一些job,比如在多線程環(huán)境下可以使用全局變量之類的

創(chuàng)建一個測試任務,這里在網上看到大部分都是直接@Scope(“prototype”),這里測試是不生效的,再加上proxyMode才行,代碼如下

import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class TestAsyncClient {
    private int a = 0;
    @Async
    public void test(int a) {
        this.a = a;
        CommonAsyncJobs.list.add(this.a + "");
    }
}

測試

import cn.hutool.core.collection.CollectionUtil;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Set;
import java.util.Vector;
@Slf4j
@EnableAsync
@SpringBootTest
@RunWith(SpringRunner.class)
public class CommonAsyncJobs {
    @Autowired
    private TestAsyncClient testAsyncClient;
    // 多線程環(huán)境下,普通的list.add不適用,用Vector處理就行了,效率低,但無所謂反正測試的
    public static Vector<String> list = new Vector<>();
    @Test
    public void testAsync() throws Exception {
        // 循環(huán)里面異步處理
        int a = 100000;
        for (int i = 0; i < a; i++) {
            testAsyncClient.test(i);
        }
        System.out.println("多線程結果:" + list.size());
        System.out.println("單線程結果:" + a);
        Set<String> set = CollectionUtil.newHashSet();
        Set<String> exist = CollectionUtil.newHashSet();
        for (String s : list) {
            if (set.contains(s)) {
                exist.add(s);
            } else {
                set.add(s);
            }
        }
        // 沒重復的值,說明多線程環(huán)境下,全局變量沒有問題
        System.out.println("重復的值:" + exist.size());
        System.out.println("重復的值:" + exist);
        // 單元測試內主線程結束會終止子線程任務
        Thread.sleep(Long.MAX_VALUE);
    }
}

結果

沒重復的值,說明多線程環(huán)境下,全局變量沒有問題

在這里插入圖片描述

@Scope(“prototype“)正確用法——解決Bean多例問題

1.問題,Spring管理的某個Bean需要使用多例

在使用了Spring的web工程中,除非特殊情況,我們都會選擇使用Spring的IOC功能來管理Bean,而不是用到時去new一個。

Spring管理的Bean默認是單例的(即Spring創(chuàng)建好Bean,需要時就拿來用,而不是每次用到時都去new,又快性能又好),但有時候單例并不滿足要求(比如Bean中不全是方法,有成員,使用單例會有線程安全問題,可以搜索線程安全與線程不安全的相關文章),你上網可以很容易找到解決辦法,即使用@Scope("prototype")注解,可以通知Spring把被注解的Bean變成多例

如下所示:

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
 
@RestController
@RequestMapping(value = "/testScope")
public class TestScope {
 
    private String name;
    @RequestMapping(value = "/{username}",method = RequestMethod.GET)
    public void userProfile(@PathVariable("username") String username) {
        name = username;
        try {
            for(int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getId() + "name:" + name);
                Thread.sleep(2000);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return;
    }
}

分別發(fā)送請求http://localhost:8043/testScope/aaa和http://localhost:8043/testScope/bbb,控制臺輸出:

34name:aaa
34name:aaa
35name:bbb
34name:bbb

(34和35是兩個線程的ID,每次運行都可能不同,但是兩個請求使用的線程的ID肯定不一樣,可以用來區(qū)分兩個請求。)可以看到第二個請求bbb開始后,將name的內容改為了“bbb”,第一個請求的name也從“aaa”改為了“bbb”。要想避免這種情況,可以使用@Scope("prototype"),注解加在TestScope這個類上。加完注解后重復上面的請求,發(fā)現(xiàn)第一個請求一直輸出“aaa”,第二個請求一直輸出“bbb”,成功。

2.問題升級

多個Bean的依賴鏈中,有一個需要多例

第一節(jié)中是一個很簡單的情況,真實的Spring Web工程起碼有Controller、Service、Dao三層,假如Controller層是單例,Service層需要多例,這時候應該怎么辦呢?

2.1一次失敗的嘗試

首先我們想到的是在Service層加注解@Scope("prototype"),如下所示:

controller類代碼

import com.example.test.service.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
 
@RestController
@RequestMapping(value = "/testScope")
public class TestScope {
 
    @Autowired
    private Order order;
 
    private String name;
 
    @RequestMapping(value = "/{username}", method = RequestMethod.GET)
    public void userProfile(@PathVariable("username") String username) {
        name = username;
        order.setOrderNum(name);
        try {
            for (int i = 0; i < 100; i++) {
                System.out.println(
                        Thread.currentThread().getId()
                                + "name:" + name
                                + "--order:"
                                + order.getOrderNum());
                Thread.sleep(2000);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return;
    }
}

Service類代碼

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
 
@Service
@Scope("prototype")
public class Order {
    private String orderNum;
 
    public String getOrderNum() {
        return orderNum;
    }
 
    public void setOrderNum(String orderNum) {
        this.orderNum = orderNum;
    }
 
    @Override
    public String toString() {
        return "Order{" +
                "orderNum='" + orderNum + '\'' +
                '}';
    }
}

分別發(fā)送請求http://localhost:8043/testScope/aaa和http://localhost:8043/testScope/bbb,控制臺輸出:

32name:aaa--order:aaa
32name:aaa--order:aaa
34name:bbb--order:bbb
32name:bbb--order:bbb

可以看到Controller的name和Service的orderNum都被第二個請求從“aaa”改成了“bbb”,Service并不是多例,失敗。

2.2 一次成功的嘗試

我們再次嘗試,在Controller和Service都加上@Scope("prototype"),結果成功,這里不重復貼代碼,讀者可以自己試試。

2.3 成功的原因(對2.1、2.2的理解)

Spring定義了多種作用域,可以基于這些作用域創(chuàng)建bean,包括:

  • 單例( Singleton):在整個應用中,只創(chuàng)建bean的一個實例。
  • 原型( Prototype):每次注入或者通過Spring應用上下文獲取的時候,都會創(chuàng)建一個新的bean實例。

對于以上說明,我們可以這樣理解:雖然Service是多例的,但是Controller是單例的。如果給一個組件加上@Scope("prototype")注解,每次請求它的實例,spring的確會給返回一個新的。問題是這個多例對象Service是被單例對象Controller依賴的。而單例服務Controller初始化的時候,多例對象Service就已經注入了;當你去使用Controller的時候,Service也不會被再次創(chuàng)建了(注入時創(chuàng)建,而注入只有一次)。

2.4 另一種成功的嘗試(基于2.3的猜想)

為了驗證2.3的猜想,我們在Controller鐘每次去請求獲取Service實例,而不是使用@Autowired注入,代碼如下:

Controller類

import com.example.test.service.Order;
import com.example.test.utils.SpringBeanUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
 
@RestController
@RequestMapping(value = "/testScope")
public class TestScope {
 
    private String name;
 
    @RequestMapping(value = "/{username}", method = RequestMethod.GET)
    public void userProfile(@PathVariable("username") String username) {
        name = username;
        Order order = SpringBeanUtil.getBean(Order.class);
        order.setOrderNum(name);
        try {
            for (int i = 0; i < 100; i++) {
                System.out.println(
                        Thread.currentThread().getId()
                                + "name:" + name
                                + "--order:"
                                + order.getOrderNum());
                Thread.sleep(2000);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return;
    }
}
 

用于獲取Spring管理的Bean的類

package com.example.test.utils;
 
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
 
@Component
public class SpringBeanUtil implements ApplicationContextAware {
 
    /**
     * 上下文對象實例
     */
    private static ApplicationContext applicationContext;
 
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
 
    /**
     * 獲取applicationContext
     *
     * @return
     */
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }
 
    /**
     * 通過name獲取 Bean.
     *
     * @param name
     * @return
     */
    public static Object getBean(String name) {
        return getApplicationContext().getBean(name);
    }
 
    /**
     * 通過class獲取Bean.
     *
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(Class<T> clazz) {
        return getApplicationContext().getBean(clazz);
    }
 
    /**
     * 通過name,以及Clazz返回指定的Bean
     *
     * @param name
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(String name, Class<T> clazz) {
        return getApplicationContext().getBean(name, clazz);
    }
}

Order的代碼不變。

分別發(fā)送請求http://localhost:8043/testScope/aaa和http://localhost:8043/testScope/bbb,控制臺輸出:

31name:aaa--order:aaa
33name:bbb--order:bbb
31name:bbb--order:aaa
33name:bbb--order:bbb

可以看到,第二次請求的不會改變第一次請求的name和orderNum。問題解決。我們在2.3節(jié)中給出的的理解是對的。

3. Spring給出的解決問題的辦法(解決Bean鏈中某個Bean需要多例的問題)

雖然第二節(jié)解決了問題,但是有兩個問題:

  • 方法一,為了一個多例,讓整個一串Bean失去了單例的優(yōu)勢;
  • 方法二,破壞IOC注入的優(yōu)美展現(xiàn)形式,和new一樣不便于管理和修改。

Spring作為一個優(yōu)秀的、用途廣、發(fā)展時間長的框架,一定有成熟的解決辦法。經過一番搜索,我們發(fā)現(xiàn),注解@Scope("prototype")(這個注解實際上也可以寫成@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE,使用常量比手打字符串不容易出錯)還有很多用法。

首先value就分為四類:

  • ConfigurableBeanFactory.SCOPE_PROTOTYPE,即“prototype”
  • ConfigurableBeanFactory.SCOPE_SINGLETON,即“singleton”
  • WebApplicationContext.SCOPE_REQUEST,即“request”
  • WebApplicationContext.SCOPE_SESSION,即“session”

他們的含義是:

  • singleton和prototype分別代表單例和多例;
  • request表示請求,即在一次http請求中,被注解的Bean都是同一個Bean,不同的請求是不同的Bean;
  • session表示會話,即在同一個會話中,被注解的Bean都是使用的同一個Bean,不同的會話使用不同的Bean。

使用session和request產生了一個新問題,生成controller的時候需要service作為controller的成員,但是service只在收到請求(可能是request也可能是session)時才會被實例化,controller拿不到service實例。為了解決這個問題,@Scope注解添加了一個proxyMode的屬性,有兩個值ScopedProxyMode.INTERFACES和ScopedProxyMode.TARGET_CLASS,前一個表示表示Service是一個接口,后一個表示Service是一個類。

本文遇到的問題中,將@Scope注解改成@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)就可以了,這里就不重復貼代碼了。

問題解決。以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • java時間戳轉日期格式的實現(xiàn)代碼

    java時間戳轉日期格式的實現(xiàn)代碼

    本篇文章是對java時間戳轉日期格式的實現(xiàn)代碼進行了詳細的分析介紹,需要的朋友參考下
    2013-06-06
  • Java獲得當前時間前指定幾個小時具體時間的方法示例

    Java獲得當前時間前指定幾個小時具體時間的方法示例

    這篇文章主要介紹了Java獲得當前時間前指定幾個小時具體時間的方法,涉及java使用Calendar針對日期時間的相關運算與轉換操作技巧,需要的朋友可以參考下
    2017-08-08
  • Java 模擬真正的并發(fā)請求詳情

    Java 模擬真正的并發(fā)請求詳情

    有時需要測試一下某個功能的并發(fā)性能,又不要想借助于其他工具,索性就自己的開發(fā)語言,來一個并發(fā)請求就最方便了。下文我們就來學習Java 如何模擬真正的并發(fā)請求
    2021-09-09
  • JAVA十大排序算法之歸并排序詳解

    JAVA十大排序算法之歸并排序詳解

    這篇文章主要介紹了java中的歸并排序,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-08-08
  • Java8函數(shù)式編程應用小結

    Java8函數(shù)式編程應用小結

    Java8非常重要的就是引入了函數(shù)式編程的思想,使得這門經典的面向對象語言有了函數(shù)式的編程方式,彌補了很大程度上的不足,函數(shù)式思想在處理復雜問題上有著更為令人稱贊的特性,本文給大家介紹Java8函數(shù)式編程應用小結,感興趣的朋友一起看看吧
    2023-12-12
  • Java?ServletContext與ServletConfig接口使用教程

    Java?ServletContext與ServletConfig接口使用教程

    ServletConfig對象,叫Servlet配置對象。主要用于加載配置文件的初始化參數(shù)。我們知道一個Web應用里面可以有多個servlet,如果現(xiàn)在有一份數(shù)據需要傳給所有的servlet使用,那么我們就可以使用ServletContext對象了
    2022-09-09
  • eclipse輸出Hello World的實現(xiàn)方法

    eclipse輸出Hello World的實現(xiàn)方法

    這篇文章主要介紹了eclipse輸出Hello World的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-11-11
  • SpringBoot使用Mybatis注解實現(xiàn)分頁動態(tài)sql開發(fā)教程

    SpringBoot使用Mybatis注解實現(xiàn)分頁動態(tài)sql開發(fā)教程

    這篇文章主要為大家介紹了SpringBoot使用Mybatis注解實現(xiàn)分頁及動態(tài)sql開發(fā)教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-03-03
  • HashMap鏈表與紅黑樹轉換詳解

    HashMap鏈表與紅黑樹轉換詳解

    這篇文章主要介紹了HashMap鏈表與紅黑樹轉換詳解,HashMap是Java中的一種數(shù)據結構,它實現(xiàn)了Map接口,提供了鍵值對的存儲和檢索功能,它基于哈希表的原理,通過將鍵映射到哈希表中的位置來存儲和獲取值,從而實現(xiàn)了快速的查找和插入操作,需要的朋友可以參考下
    2023-11-11
  • Springboot @Configuration與自動配置詳解

    Springboot @Configuration與自動配置詳解

    這篇文章主要介紹了SpringBoot中的@Configuration自動配置,在進行項目編寫前,我們還需要知道一個東西,就是SpringBoot對我們的SpringMVC還做了哪些配置,包括如何擴展,如何定制,只有把這些都搞清楚了,我們在之后使用才會更加得心應手
    2022-07-07

最新評論