最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深度解析Spring內(nèi)置作用域及其在實踐中的應(yīng)用

 更新時間:2023年06月13日 10:21:38   作者:磚業(yè)洋__  
這篇文章主要詳細介紹了Spring內(nèi)置的作用域類型及其在實踐中的應(yīng)用,文中有詳細的代碼示例,對我們的餓學(xué)習(xí)或工作有一定的參考價值,感興趣的同學(xué)可以借鑒閱讀

1. Spring的內(nèi)置作用域

我們來看看Spring內(nèi)置的作用域類型。在5.x版本中,Spring內(nèi)置了六種作用域:

  • singleton:在IOC容器中,對應(yīng)的Bean只有一個實例,所有對它的引用都指向同一個對象。這種作用域非常適合對于無狀態(tài)的Bean,比如工具類或服務(wù)類。
  • prototype:每次請求都會創(chuàng)建一個新的Bean實例,適合對于需要維護狀態(tài)的Bean。
  • request:在Web應(yīng)用中,為每個HTTP請求創(chuàng)建一個Bean實例。適合在一個請求中需要維護狀態(tài)的場景,如跟蹤用戶行為信息。
  • session:在Web應(yīng)用中,為每個HTTP會話創(chuàng)建一個Bean實例。適合需要在多個請求之間維護狀態(tài)的場景,如用戶會話。
  • application:在整個Web應(yīng)用期間,創(chuàng)建一個Bean實例。適合存儲全局的配置數(shù)據(jù)等。
  • websocket:在每個WebSocket會話中創(chuàng)建一個Bean實例。適合WebSocket通信場景。

我們需要重點學(xué)習(xí)兩種作用域:singletonprototype。在大多數(shù)情況下singletonprototype這兩種作用域已經(jīng)足夠滿足需求。

2. singleton作用域

2.1 singleton作用域的定義和用途

SingletonSpring的默認作用域。在這個作用域中,Spring容器只會創(chuàng)建一個實例,所有對該bean的請求都將返回這個唯一的實例。

例如,我們定義一個名為Plaything的類,并將其作為一個bean

@Component
public class Plaything {
    public Plaything() {
        System.out.println("Plaything constructor run ...");
    }
}

在這個例子中,Plaything是一個singleton作用域的bean。無論我們在應(yīng)用中的哪個地方請求這個bean,Spring都會返回同一個Plaything實例。

下面的例子展示了如何創(chuàng)建一個單實例的Bean

package com.example.demo.bean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Kid {
    private Plaything plaything;
    @Autowired
    public void setPlaything(Plaything plaything) {
        this.plaything = plaything;
    }
    public Plaything getPlaything() {
        return plaything;
    }
}
package com.example.demo.bean;
import org.springframework.stereotype.Component;
@Component
public class Plaything {
    public Plaything() {
        System.out.println("Plaything constructor run ...");
    }
}

這里可以在Plaything類加上@Scope(BeanDefinition.SCOPE_SINGLETON),但是因為是默認作用域是Singleton,所以沒必要加。

package com.example.demo.configuration;
import com.example.demo.bean.Kid;
import com.example.demo.bean.Plaything;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class BeanScopeConfiguration {
    @Bean
    public Kid kid1(Plaything plaything1) {
        Kid kid = new Kid();
        kid.setPlaything(plaything1);
        return kid;
    }
    @Bean
    public Kid kid2(Plaything plaything2) {
        Kid kid = new Kid();
        kid.setPlaything(plaything2);
        return kid;
    }
}
package com.example.demo.application;
import com.example.demo.bean.Kid;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan("com.example")
public class DemoApplication {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(DemoApplication.class);
        context.getBeansOfType(Kid.class).forEach((name, kid) -> {
            System.out.println(name + " : " + kid.getPlaything());
        });
    }
}

在Spring IoC容器的工作中,掃描過程只會創(chuàng)建bean的定義,真正的bean實例是在需要注入或者通過getBean方法獲取時才會創(chuàng)建。這個過程被稱為bean的初始化。

這里運行 ctx.getBeansOfType(Kid.class).forEach((name, kid) -> System.out.println(name + " : " + kid.getPlaything())); 時,Spring IoC容器會查找所有的Kid類型的bean定義,然后為每一個找到的bean定義創(chuàng)建實例(如果這個bean定義還沒有對應(yīng)的實例),并注入相應(yīng)的依賴。

運行結(jié)果:

三個 Kid 的 Plaything bean是相同的,說明默認情況下 Plaything 是一個單例bean,整個Spring應(yīng)用中只有一個 Plaything bean被創(chuàng)建。

為什么會有3kid?

  • Kid: 這個是通過在Kid類上標注的@Component注解自動創(chuàng)建的。Spring在掃描時發(fā)現(xiàn)這個注解,就會自動在IOC容器中注冊這個bean。這個Bean的名字默認是將類名的首字母小寫kid。
  • kid1: 在 BeanScopeConfiguration 中定義,通過kid1(Plaything plaything1)方法創(chuàng)建,并且注入了plaything1。
  • kid2: 在 BeanScopeConfiguration 中定義,通過kid2(Plaything plaything2)方法創(chuàng)建,并且注入了plaything2。

2.2 singleton作用域線程安全問題

需要注意的是,雖然singleton Bean只會有一個實例,但Spring并不會解決其線程安全問題,開發(fā)者需要根據(jù)實際場景自行處理。

我們通過一個代碼示例來說明在多線程環(huán)境中出現(xiàn)singleton Bean的線程安全問題。

首先,我們創(chuàng)建一個名為Countersingleton Bean,這個Bean有一個count變量,提供increment方法來增加count的值:

package com.example.demo.bean;
import org.springframework.stereotype.Component;
@Component
public class Counter {
    private int count = 0;
    public int increment() {
        return ++count;
    }
}

然后,我們創(chuàng)建一個名為CounterServicesingleton Bean,這個Bean依賴于Counter,在increaseCount方法中,我們調(diào)用counter.increment方法:

package com.example.demo.service;
import com.example.demo.bean.Counter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class CounterService {
	@Autowired
    private final Counter counter;
    public void increaseCount() {
        counter.increment();
    }
}

我們在多線程環(huán)境中調(diào)用counterService.increaseCount方法時,就可能出現(xiàn)線程安全問題。因為counter.increment方法并非線程安全,多個線程同時調(diào)用此方法可能會導(dǎo)致count值出現(xiàn)預(yù)期外的結(jié)果。

要解決這個問題,我們需要使counter.increment方法線程安全。

這里可以使用原子變量,在Counter類中,我們可以使用AtomicInteger來代替int類型的count,因為AtomicInteger類中的方法是線程安全的,且其性能通常優(yōu)于synchronized關(guān)鍵字。

package com.example.demo.bean;
import org.springframework.stereotype.Component;
import java.util.concurrent.atomic.AtomicInteger;
@Component
public class Counter {
    private AtomicInteger count = new AtomicInteger(0);
    public int increment() {
        return count.incrementAndGet();
    }
}

盡管優(yōu)化后已經(jīng)使Counter類線程安全,但在設(shè)計Bean時,我們應(yīng)該盡可能地減少可變狀態(tài)。這是因為可變狀態(tài)使得并發(fā)編程變得復(fù)雜,而無狀態(tài)的Bean通常更容易理解和測試。

什么是無狀態(tài)的Bean呢? 如果一個Bean不持有任何狀態(tài)信息,也就是說,同樣的輸入總是會得到同樣的輸出,那么這個Bean就是無狀態(tài)的。反之,則是有狀態(tài)的Bean

3. prototype作用域

3.1 prototype作用域的定義和用途

prototype作用域中,Spring容器會為每個請求創(chuàng)建一個新的bean實例。

例如,我們定義一個名為Plaything的類,并將其作用域設(shè)置為prototype

package com.example.demo.bean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class Plaything {
    public Plaything() {
        System.out.println("Plaything constructor run ...");
    }
}

在這個例子中,Plaything是一個prototype作用域的bean。每次我們請求這個bean,Spring都會創(chuàng)建一個新的Plaything實例。

我們只需要修改上面的Plaything類,其他的類不用動。

打印結(jié)果:

這個@Scope(BeanDefinition.SCOPE_PROTOTYPE)可以寫成@Scope("prototype"),按照規(guī)范,還是利用已有的常量比較好。

3.2 prototype作用域在開發(fā)中的例子

以我個人來說,我在excel多線程上傳的時候用到過這個,當時是EasyExcel框架,我給一部分關(guān)鍵代碼展示一下如何在Spring中使用prototype作用域來處理多線程環(huán)境下的任務(wù)(實際業(yè)務(wù)會更復(fù)雜),大家可以對比,如果用prototype作用域和使用new對象的形式在實際開發(fā)中有什么區(qū)別。

使用prototype作用域的例子

@Resource
private ApplicationContext context;
@PostMapping("/user/upload")
public ResultModel upload(@RequestParam("multipartFile") MultipartFile multipartFile) {
	......
	ExecutorService es = new ThreadPoolExceutor(10, 16, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(2000), new ThreadPoolExecutor.CallerRunPolicy());
	......
	EasyExcel.read(multipartFile.getInputStream(), UserDataUploadVO.class, 
		new PageReadListener<UserDataUploadVO>(dataList ->{
		......
		// 多線程處理上傳excel數(shù)據(jù)
			Future<?> future = es.submit(context.getBean(AsyncUploadHandler.class, user, dataList, errorCount));
		......
		})).sheet().doRead();
	......
}

AsyncUploadHandler.java

@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class AsyncUploadHandler implements Runnable {
	private User user;
	private List<UserDataUploadVO> dataList;
	private AtomicInteger errorCount;
	@Resource
	private RedisService redisService;
	@Resource
	private CompanyManagementMapper companyManagementMapper;
	public AsyncUploadHandler(user, List<UserDataUploadVO> dataList, AtomicInteger errorCount) {
		this.user = user;
		this.dataList = dataList;
		this.errorCount = errorCount;
	}
	......
}

AsyncUploadHandler類是一個prototype作用域的bean,它被用來處理上傳的Excel數(shù)據(jù)。由于并發(fā)上傳的每個任務(wù)可能需要處理不同的數(shù)據(jù),并且可能需要在不同的用戶上下文中執(zhí)行,因此每個任務(wù)都需要有自己的AsyncUploadHandler bean。這就是為什么需要將AsyncUploadHandler定義為prototype作用域的原因。

由于AsyncUploadHandler是由Spring管理的,我們可以直接使用@Resource注解來注入其他的bean,例如RedisServiceCompanyManagementMapper。

AsyncUploadHandler交給Spring容器管理,里面依賴的容器對象可以直接用@Resource注解注入。如果采用new出來的對象,那么這些對象只能從外面注入好了再傳入進去。

不使用prototype作用域改用new對象的例子

@PostMapping("/user/upload")
public ResultModel upload(@RequestParam("multipartFile") MultipartFile multipartFile) {
	......
	ExecutorService es = new ThreadPoolExceutor(10, 16, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(2000), new ThreadPoolExecutor.CallerRunPolicy());
	......
	EasyExcel.read(multipartFile.getInputStream(), UserDataUploadVO.class, 
		new PageReadListener<UserDataUploadVO>(dataList ->{
		......
		// 多線程處理上傳excel數(shù)據(jù)
			Future<?> future = es.submit(new AsyncUploadHandler(user, dataList, errorCount, redisService, companyManagementMapper));
		......
		})).sheet().doRead();
	......
}

AsyncUploadHandler.java

public class AsyncUploadHandler implements Runnable {
	private User user;
	private List<UserDataUploadVO> dataList;
	private AtomicInteger errorCount;
	private RedisService redisService;
	private CompanyManagementMapper companyManagementMapper;
	public AsyncUploadHandler(user, List<UserDataUploadVO> dataList, AtomicInteger errorCount, 
						RedisService redisService, CompanyManagementMapper companyManagementMapper) {
		this.user = user;
		this.dataList = dataList;
		this.errorCount = errorCount;
		this.redisService = redisService;
		this.companyManagementMapper = companyManagementMapper;
	}
	......
}

如果直接新建AsyncUploadHandler對象,則需要手動傳入所有的依賴,這會使代碼變得更復(fù)雜更難以管理,而且還需要手動管理AsyncUploadHandler的生命周期。

4. request作用域(了解)

request作用域:Bean在一個HTTP請求內(nèi)有效。當請求開始時,Spring容器會為每個新的HTTP請求創(chuàng)建一個新的Bean實例,這個Bean在當前HTTP請求內(nèi)是有效的,請求結(jié)束后,Bean就會被銷毀。如果在同一個請求中多次獲取該Bean,就會得到同一個實例,但是在不同的請求中獲取的實例將會不同。

@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestScopedBean {
    // 在一次Http請求內(nèi)共享的數(shù)據(jù)
    private String requestData;
    public void setRequestData(String requestData) {
        this.requestData = requestData;
    }
    public String getRequestData() {
        return this.requestData;
    }
}

上述Bean在一個HTTP請求的生命周期內(nèi)是一個單例,每個新的HTTP請求都會創(chuàng)建一個新的Bean實例。

5. session作用域(了解)

session作用域:Bean是在同一個HTTP會話(Session)中是單例的。也就是說,從用戶登錄開始,到用戶退出登錄(或者Session超時)結(jié)束,這個過程中,不管用戶進行了多少次HTTP請求,只要是在同一個會話中,都會使用同一個Bean實例。

@Component
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class SessionScopedBean {
    // 在一個Http會話內(nèi)共享的數(shù)據(jù)
    private String sessionData;
    public void setSessionData(String sessionData) {
        this.sessionData = sessionData;
    }
    public String getSessionData() {
        return this.sessionData;
    }
}

這樣的設(shè)計對于存儲和管理會話級別的數(shù)據(jù)非常有用,例如用戶的登錄信息、購物車信息等。因為它們是在同一個會話中保持一致的,所以使用session作用域的Bean可以很好地解決這個問題。

但是實際開發(fā)中沒人這么干,會話id都會存在數(shù)據(jù)庫,根據(jù)會話id就能在各種表中獲取數(shù)據(jù),避免頻繁查庫也是把關(guān)鍵信息序列化后存在Redis。

6. application作用域(了解)

application作用域:在整個Web應(yīng)用的生命周期內(nèi),Spring容器只會創(chuàng)建一個Bean實例。這個BeanWeb應(yīng)用的生命周期內(nèi)都是有效的,當Web應(yīng)用停止后,Bean就會被銷毀。

@Component
@Scope(value = WebApplicationContext.SCOPE_APPLICATION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ApplicationScopedBean {
    // 在整個Web應(yīng)用的生命周期內(nèi)共享的數(shù)據(jù)
    private String applicationData;
    public void setApplicationData(String applicationData) {
        this.applicationData = applicationData;
    }
    public String getApplicationData() {
        return this.applicationData;
    }
}

如果在一個application作用域的Bean上調(diào)用setter方法,那么這個變更將對所有用戶和會話可見。后續(xù)對這個Bean的所有調(diào)用(包括gettersetter)都將影響到同一個Bean實例,后面的調(diào)用會覆蓋前面的狀態(tài)。

7. websocket作用域(了解)

websocket作用域:Bean 在每一個新的 WebSocket 會話中都會被創(chuàng)建一次,就像 session 作用域的 Bean 在每一個 HTTP 會話中都會被創(chuàng)建一次一樣。這個Bean在整個WebSocket會話內(nèi)都是有效的,當WebSocket會話結(jié)束后,Bean就會被銷毀。

@Component
@Scope(value = "websocket", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class WebSocketScopedBean {
    // 在一個WebSocket會話內(nèi)共享的數(shù)據(jù)
    private String socketData;
    public void setSocketData(String socketData) {
        this.socketData = socketData;
    }
    public String getSocketData() {
        return this.socketData;
    }
}

上述Bean在一個WebSocket會話的生命周期內(nèi)是一個單例,每個新的WebSocket會話都會創(chuàng)建一個新的Bean實例。

這個作用域需要Spring Websocket模塊支持,并且應(yīng)用需要配置為使用websocket。

以上就是深度解析Spring內(nèi)置作用域及其在實踐中的應(yīng)用的詳細內(nèi)容,更多關(guān)于Spring內(nèi)置作用域及應(yīng)用的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • spring定時任務(wù)(scheduler)的串行、并行執(zhí)行實現(xiàn)解析

    spring定時任務(wù)(scheduler)的串行、并行執(zhí)行實現(xiàn)解析

    這篇文章主要介紹了spring定時任務(wù)(scheduler)的串行、并行執(zhí)行實現(xiàn)解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-09-09
  • Java中的通用路徑轉(zhuǎn)義符介紹

    Java中的通用路徑轉(zhuǎn)義符介紹

    這篇文章主要介紹了Java中的通用路徑轉(zhuǎn)義符介紹,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Idea打不了斷點如何解決

    Idea打不了斷點如何解決

    這篇文章主要介紹了Idea打不了斷點如何解決的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • java線程池使用及原理面試題

    java線程池使用及原理面試題

    很多面試官喜歡把線程池作為問題的起點,然后延伸到其它內(nèi)容,由于我們專欄已經(jīng)說過隊列、線程、鎖面試題了,所以本章面試題還是以線程池為主
    2022-03-03
  • Feign的原理及概念詳解

    Feign的原理及概念詳解

    文章介紹了Feign,一個聲明式、模板化的HTTP客戶端,以及其在Spring Cloud中的增強,Feign通過代理類簡化HTTP請求的調(diào)用,并提供了詳細的調(diào)用流程、優(yōu)化配置和架構(gòu)說明,幫助開發(fā)者更方便地進行服務(wù)調(diào)用和管理
    2025-11-11
  • mybatisPlus實現(xiàn)倒序拼接字符串

    mybatisPlus實現(xiàn)倒序拼接字符串

    這篇文章主要介紹了mybatisPlus實現(xiàn)倒序拼接字符串方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • Java日常開發(fā)中讀寫TXT文本舉例詳解

    Java日常開發(fā)中讀寫TXT文本舉例詳解

    這篇文章主要給大家介紹了關(guān)于Java日常開發(fā)中讀寫TXT文本,包括使用BufferedReader、Scanner、FileInputStream等類進行讀取,以及使用BufferedWriter、PrintWriter、FileOutputStream等類進行寫入,需要的朋友可以參考下
    2024-12-12
  • Java日常練習(xí)題,每天進步一點點(10)

    Java日常練習(xí)題,每天進步一點點(10)

    下面小編就為大家?guī)硪黄狫ava基礎(chǔ)的幾道練習(xí)題(分享)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧,希望可以幫到你
    2021-07-07
  • Java流操作之數(shù)據(jù)流實例代碼

    Java流操作之數(shù)據(jù)流實例代碼

    這篇文章主要介紹了Java流操作之數(shù)據(jù)流實例代碼,具有一定借鑒價值,需要的朋友可以參考下
    2018-01-01
  • 在Java的MyBatis框架中建立接口進行CRUD操作的方法

    在Java的MyBatis框架中建立接口進行CRUD操作的方法

    這篇文章主要介紹了在Java的MyBatis框架中建立接口進行CRUD操作的方法,CRUD是指在做計算處理時的增加(Create)、重新取得數(shù)據(jù)(Retrieve)、更新(Update)和刪除(Delete)幾個單詞的首字母簡寫,需要的朋友可以參考下
    2016-04-04

最新評論

威宁| 翁源县| 贵德县| 苍山县| 巴彦县| 洪江市| 布尔津县| 永和县| 江门市| 塔河县| 繁昌县| 泰安市| 雷山县| 高台县| 小金县| 嫩江县| 崇文区| 宜良县| 积石山| 海淀区| 鞍山市| 沧源| 南平市| 清涧县| 仁布县| 保山市| 伊宁县| 金寨县| 古田县| 古田县| 闸北区| 赣州市| 文登市| 绥中县| 敦煌市| 祁阳县| 东山县| 梁平县| 汶上县| 泾源县| 周至县|