如何在Spring中自定義scope的方法示例
大家對(duì)于 Spring 的 scope 應(yīng)該都不會(huì)默認(rèn)。所謂 scope,字面理解就是“作用域”、“范圍”,如果一個(gè) bean 的 scope 配置為 singleton,則從容器中獲取 bean 返回的對(duì)象都是相同的;如果 scope 配置為prototype,則每次返回的對(duì)象都不同。
一般情況下,Spring 提供的 scope 都能滿(mǎn)足日常應(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ì)話(huà)。只有在使用有 Web 能力的 Spring ApplicationContext 才有效 |
| application | 定義了每個(gè) ServletContext 一個(gè)實(shí)例 |
| websocket | 定義了每個(gè) WebSocket 一個(gè)實(shí)例。只有在使用有 Web 能力的 Spring ApplicationContext 才有效 |
如果上述 scope 仍然不能滿(mǎn)足你的需求,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 方法即可。
自定義線(xiàn)程范圍內(nèi)的scope
現(xiàn)在進(jìn)入實(shí)戰(zhàn)環(huán)節(jié)。我們要自定義一個(gè)Spring沒(méi)有的scope,該scope將bean的作用范圍限制在了線(xiàn)程內(nèi)。即,相同線(xiàn)程內(nèi)的bean是同個(gè)對(duì)象,跨線(xiàn)程則是不同的對(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 <a rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >Way Lau</a>
*/
public class ThreadScope implements Scope {
private final ThreadLocal<Map<String, Object>> threadLoacal = new ThreadLocal<Map<String, Object>>() {
@Override
protected Map<String, Object> initialValue() {
return new HashMap<String, Object>();
}
};
public Object get(String name, ObjectFactory<?> objectFactory) {
Map<String, Object> scope = threadLoacal.get();
Object obj = scope.get(name);
// 不存在則放入ThreadLocal
if (obj == null) {
obj = objectFactory.getObject();
scope.put(name, obj);
System.out.println("Not exists " + name + "; hashCode: " + obj.hashCode());
} else {
System.out.println("Exists " + name + "; hashCode: " + obj.hashCode());
}
return obj;
}
public Object remove(String name) {
Map<String, Object> scope = threadLoacal.get();
return scope.remove(name);
}
public String getConversationId() {
return null;
}
public void registerDestructionCallback(String arg0, Runnable arg1) {
}
public Object resolveContextualObject(String arg0) {
return null;
}
}
在上述代碼中,threadLoacal用于做線(xiàn)程之間的數(shù)據(jù)隔離。換言之,threadLoacal實(shí)現(xiàn)了相同的線(xiàn)程相同名字的bean是同一個(gè)對(duì)象;不同的線(xiàn)程的相同名字的bean是不同的對(duì)象。
同時(shí),我們將對(duì)象的hashCode打印了出來(lái)。如果他們是相同的對(duì)象,則hashCode是相同的。
2. 注冊(cè)scope
定義一個(gè)AppConfig配置類(lèi),將自定義的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 <a rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >Way Lau</a>
*/
@Configuration
@ComponentScan
public class AppConfig {
@Bean
public static CustomScopeConfigurer customScopeConfigurer() {
CustomScopeConfigurer customScopeConfigurer = new CustomScopeConfigurer();
Map<String, Object> map = new HashMap<String, Object>();
map.put("threadScope", new ThreadScope());
// 配置scope
customScopeConfigurer.setScopes(map);
return customScopeConfigurer;
}
}
“threadScope”就是自定義ThreadScope的名稱(chēng)。
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 <a rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >Way Lau</a>
*/
@Scope("threadScope")
@Service
public class MessageServiceImpl implements MessageService {
public String getMessage() {
return "Hello World!";
}
}
其中@Scope("threadScope")中的“threadScope”就是自定義ThreadScope的名稱(chēng)。
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 <a rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >Way Lau</a>
*/
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“相同的線(xiàn)程相同名字的bean是同一個(gè)對(duì)象”。
如果想繼續(xù)驗(yàn)證ThreadScope“不同的線(xiàn)程的相同名字的bean是不同的對(duì)象”,則只需要將Application改造為多線(xiàn)程即可。
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 <a rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >Way Lau</a>
*/
public class Application {
public static void main(String[] args) throws InterruptedException, ExecutionException {
@SuppressWarnings("resource")
ApplicationContext context =
new AnnotationConfigApplicationContext(AppConfig.class);
CompletableFuture<String> task1 = 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<String> 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“相同的線(xiàn)程相同名字的bean是同一個(gè)對(duì)象;不同的線(xiàn)程的相同名字的bean是不同的對(duì)象”
源碼
見(jiàn)https://github.com/waylau/spring-5-book 的 s5-ch02-custom-scope-annotation項(xiàng)目。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java實(shí)現(xiàn)ATM機(jī)操作系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)ATM機(jī)操作系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05
Idea自動(dòng)生成Entity實(shí)現(xiàn)過(guò)程詳解
這篇文章主要介紹了Idea自動(dòng)生成Entity實(shí)現(xiàn)過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09
SpringMVC @ControllerAdvice使用場(chǎng)景
這篇文章主要介紹了SpringMVC @ControllerAdvice使用場(chǎng)景,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11
Mybatis如何解決sql中l(wèi)ike通配符模糊匹配問(wèn)題
這篇文章主要介紹了Mybatis如何解決sql中l(wèi)ike通配符模糊匹配問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01
使用Maven 搭建 Spring MVC 本地部署Tomcat的詳細(xì)教程
這篇文章主要介紹了使用Maven 搭建 Spring MVC 本地部署Tomcat,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08
使用kotlin集成springboot開(kāi)發(fā)的超詳細(xì)教程
目前大多數(shù)都在使用java集成 springboot進(jìn)行開(kāi)發(fā),本文演示僅僅將 java換成 kotlin,其他不變的情況下進(jìn)行開(kāi)發(fā),需要的朋友可以參考下2021-09-09

