十年網(wǎng)站開發(fā)經(jīng)驗(yàn) + 多家企業(yè)客戶 + 靠譜的建站團(tuán)隊(duì)
量身定制 + 運(yùn)營(yíng)維護(hù)+專業(yè)推廣+無(wú)憂售后,網(wǎng)站問(wèn)題一站解決
大家對(duì)于 Spring 的 scope 應(yīng)該都不會(huì)默認(rèn)。所謂 scope,字面理解就是“作用域”、“范圍”,如果一個(gè) bean 的 scope 配置為 singleton,則從容器中獲取 bean 返回的對(duì)象都是相同的;如果 scope 配置為prototype,則每次返回的對(duì)象都不同。
10余年的定襄網(wǎng)站建設(shè)經(jīng)驗(yàn),針對(duì)設(shè)計(jì)、前端、開發(fā)、售后、文案、推廣等六對(duì)一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。營(yíng)銷型網(wǎng)站建設(shè)的優(yōu)勢(shì)是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整定襄建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無(wú)論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。創(chuàng)新互聯(lián)從事“定襄網(wǎng)站設(shè)計(jì)”,“定襄網(wǎng)站推廣”以來(lái),每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。
一般情況下,Spring 提供的 scope 都能滿足日常應(yīng)用的場(chǎng)景。但如果你的需求極其特殊,則本文所介紹自定義 scope 合適你。
Spring 內(nèi)置的 scope
默認(rèn)時(shí),所有 Spring bean 都是的單例的,意思是在整個(gè) Spring 應(yīng)用中,bean的實(shí)例只有一個(gè)??梢栽?bean 中添加 scope 屬性來(lái)修改這個(gè)默認(rèn)值。scope 屬性可用的值如下:
范圍 | 描述 |
---|---|
singleton | 每個(gè) Spring 容器一個(gè)實(shí)例(默認(rèn)值) |
prototype | 允許 bean 可以被多次實(shí)例化(使用一次就創(chuàng)建一個(gè)實(shí)例) |
request | 定義 bean 的 scope 是 HTTP 請(qǐng)求。每個(gè) HTTP 請(qǐng)求都有自己的實(shí)例。只有在使用有 Web 能力的 Spring 上下文時(shí)才有效 |
session | 定義 bean 的 scope 是 HTTP 會(huì)話。只有在使用有 Web 能力的 Spring ApplicationContext 才有效 |
application | 定義了每個(gè) ServletContext 一個(gè)實(shí)例 |
websocket | 定義了每個(gè) WebSocket 一個(gè)實(shí)例。只有在使用有 Web 能力的 Spring ApplicationContext 才有效 |
如果上述 scope 仍然不能滿足你的需求,Spring 還預(yù)留了接口,允許你自定義 scope。
Scope 接口
org.springframework.beans.factory.config.Scope
接口用于定義scope的行為:
package org.springframework.beans.factory.config; import org.springframework.beans.factory.ObjectFactory; import org.springframework.lang.Nullable; public interface Scope { Object get(String name, ObjectFactory<?> objectFactory); @Nullable Object remove(String name); void registerDestructionCallback(String name, Runnable callback); @Nullable Object resolveContextualObject(String key); @Nullable String getConversationId(); }
一般來(lái)說(shuō),只需要重新 get 和 remove 方法即可。
自定義線程范圍內(nèi)的scope
現(xiàn)在進(jìn)入實(shí)戰(zhàn)環(huán)節(jié)。我們要自定義一個(gè)Spring沒(méi)有的scope,該scope將bean的作用范圍限制在了線程內(nèi)。即,相同線程內(nèi)的bean是同個(gè)對(duì)象,跨線程則是不同的對(duì)象。
1. 定義scope
要自定義一個(gè)Spring的scope,只需實(shí)現(xiàn) org.springframework.beans.factory.config.Scope接口。代碼如下:
package com.waylau.spring.scope; import java.util.HashMap; import java.util.Map; import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.config.Scope; /** * Thread Scope. * * @since 1.0.0 2019年2月13日 * @author Way Lau */ public class ThreadScope implements Scope { private final ThreadLocal
在上述代碼中,threadLoacal用于做線程之間的數(shù)據(jù)隔離。換言之,threadLoacal實(shí)現(xiàn)了相同的線程相同名字的bean是同一個(gè)對(duì)象;不同的線程的相同名字的bean是不同的對(duì)象。
同時(shí),我們將對(duì)象的hashCode打印了出來(lái)。如果他們是相同的對(duì)象,則hashCode是相同的。
2. 注冊(cè)scope
定義一個(gè)AppConfig配置類,將自定義的scope注冊(cè)到容器中去。代碼如下:
package com.waylau.spring.scope; import java.util.HashMap; import java.util.Map; import org.springframework.beans.factory.config.CustomScopeConfigurer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * App Config. * * @since 1.0.0 2019年2月13日 * @author Way Lau */ @Configuration @ComponentScan public class AppConfig { @Bean public static CustomScopeConfigurer customScopeConfigurer() { CustomScopeConfigurer customScopeConfigurer = new CustomScopeConfigurer(); Mapmap = new HashMap (); map.put("threadScope", new ThreadScope()); // 配置scope customScopeConfigurer.setScopes(map); return customScopeConfigurer; } }
“threadScope”就是自定義ThreadScope的名稱。
3. 使用scope
接下來(lái)就根據(jù)一般的scope的用法,來(lái)使用自定義的scope了。代碼如下:
package com.waylau.spring.scope.service; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Service; /** * Message Service Impl. * * @since 1.0.0 2019年2月13日 * @author Way Lau */ @Scope("threadScope") @Service public class MessageServiceImpl implements MessageService { public String getMessage() { return "Hello World!"; } }
其中@Scope("threadScope")中的“threadScope”就是自定義ThreadScope的名稱。
4. 定義應(yīng)用入口
定義Spring應(yīng)用入口:
package com.waylau.spring.scope; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import com.waylau.spring.scope.service.MessageService; /** * Application Main. * * @since 1.0.0 2019年2月13日 * @author Way Lau */ public class Application { public static void main(String[] args) { @SuppressWarnings("resource") ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); MessageService messageService = context.getBean(MessageService.class); messageService.getMessage(); MessageService messageService2 = context.getBean(MessageService.class); messageService2.getMessage(); } }
運(yùn)行應(yīng)用觀察控制臺(tái)輸出如下:
Not exists messageServiceImpl; hashCode: 2146338580
Exists messageServiceImpl; hashCode: 2146338580
輸出的結(jié)果也就驗(yàn)證了ThreadScope“相同的線程相同名字的bean是同一個(gè)對(duì)象”。
如果想繼續(xù)驗(yàn)證ThreadScope“不同的線程的相同名字的bean是不同的對(duì)象”,則只需要將Application改造為多線程即可。
package com.waylau.spring.scope; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import com.waylau.spring.scope.service.MessageService; /** * Application Main. * * @since 1.0.0 2019年2月13日 * @author Way Lau */ public class Application { public static void main(String[] args) throws InterruptedException, ExecutionException { @SuppressWarnings("resource") ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); CompletableFuturetask1 = CompletableFuture.supplyAsync(()->{ //模擬執(zhí)行耗時(shí)任務(wù) MessageService messageService = context.getBean(MessageService.class); messageService.getMessage(); MessageService messageService2 = context.getBean(MessageService.class); messageService2.getMessage(); //返回結(jié)果 return "result"; }); CompletableFuture task2 = CompletableFuture.supplyAsync(()->{ //模擬執(zhí)行耗時(shí)任務(wù) MessageService messageService = context.getBean(MessageService.class); messageService.getMessage(); MessageService messageService2 = context.getBean(MessageService.class); messageService2.getMessage(); //返回結(jié)果 return "result"; }); task1.get(); task2.get(); } }
觀察輸出結(jié)果;
Not exists messageServiceImpl; hashCode: 1057328090
Not exists messageServiceImpl; hashCode: 784932540
Exists messageServiceImpl; hashCode: 1057328090
Exists messageServiceImpl; hashCode: 784932540
上述結(jié)果驗(yàn)證ThreadScope“相同的線程相同名字的bean是同一個(gè)對(duì)象;不同的線程的相同名字的bean是不同的對(duì)象”
源碼
見https://github.com/waylau/spring-5-book 的 s5-ch02-custom-scope-annotation項(xiàng)目。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。