如何在 Spring 中自定义 scope
大家对于 Spring 的 scope 应该都不会默认。所谓 scope,字面理解就是“作用域”、“范围”,如果一个 bean 的 scope 配置为 singleton,则从容器中获取 bean 返回的对象都是相同的;如果 scope 配置为prototype,则每次返回的对象都不同。
一般情况下,Spring 提供的 scope 都能满足日常应用的场景。但如果你的需求极其特殊,则本文所介绍自定义 scope 合适你。
Spring 内置的 scope
默认时,所有 Spring bean 都是的单例的,意思是在整个 Spring 应用中,bean的实例只有一个。可以在 bean 中添加 scope 属性来修改这个默认值。scope 属性可用的值如下:
范围 | 描述 |
---|---|
singleton | 每个 Spring 容器一个实例(默认值) |
prototype | 允许 bean 可以被多次实例化(使用一次就创建一个实例) |
request | 定义 bean 的 scope 是 HTTP 请求。每个 HTTP 请求都有自己的实例。只有在使用有 Web 能力的 Spring 上下文时才有效 |
session | 定义 bean 的 scope 是 HTTP 会话。只有在使用有 Web 能力的 Spring ApplicationContext 才有效 |
application | 定义了每个 ServletContext 一个实例 |
websocket | 定义了每个 WebSocket 一个实例。只有在使用有 Web 能力的 Spring ApplicationContext 才有效 |
如果上述 scope 仍然不能满足你的需求,Spring 还预留了接口,允许你自定义 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); @NullableObject remove(String name); void registerDestructionCallback(String name, Runnable callback); @NullableObject resolveContextualObject(String key); @NullableString getConversationId();}
一般来说,只需要重新 get 和 remove 方法即可。
自定义线程范围内的scope
现在进入实战环节。我们要自定义一个Spring没有的scope,该scope将bean的作用范围限制在了线程内。即,相同线程内的bean是同个对象,跨线程则是不同的对象。
1. 定义scope
要自定义一个Spring的scope,只需实现 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 href="https://waylau.com">Way Lau</a>*/public class ThreadScope implements Scope { private final ThreadLocal<Map<String, Object>> threadLoacal = new ThreadLocal<Map<String, Object>>() { @Overrideprotected 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); // 不存在则放入ThreadLocalif (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用于做线程之间的数据隔离。换言之,threadLoacal实现了相同的线程相同名字的bean是同一个对象;不同的线程的相同名字的bean是不同的对象。
同时,我们将对象的hashCode打印了出来。如果他们是相同的对象,则hashCode是相同的。
2. 注册scope
定义一个AppConfig配置类,将自定义的scope注册到容器中去。代码如下:
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 href="https://waylau.com">Way Lau</a>*/@Configuration@ComponentScanpublic class AppConfig { @Beanpublic static CustomScopeConfigurer customScopeConfigurer() {CustomScopeConfigurer customScopeConfigurer = new CustomScopeConfigurer();Map<String, Object> map = new HashMap<String, Object>();map.put("threadScope", new ThreadScope()); // 配置scopecustomScopeConfigurer.setScopes(map); return customScopeConfigurer;} }
“threadScope”就是自定义ThreadScope的名称。
3. 使用scope
接下来就根据一般的scope的用法,来使用自定义的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 href="https://waylau.com">Way Lau</a>*/@Scope("threadScope")@Servicepublic class MessageServiceImpl implements MessageService { public String getMessage() { return "Hello World!";}}
其中@Scope("threadScope")
中的“threadScope”就是自定义ThreadScope的名称。
4. 定义应用入口
定义Spring应用入口:
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 href="https://waylau.com">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();}}
运行应用观察控制台输出如下:
Not exists messageServiceImpl; hashCode: 2146338580Exists messageServiceImpl; hashCode: 2146338580
输出的结果也就验证了ThreadScope“相同的线程相同名字的bean是同一个对象”。
如果想继续验证ThreadScope“不同的线程的相同名字的bean是不同的对象”,则只需要将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 <a href="https://waylau.com">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(()->{ //模拟执行耗时任务MessageService messageService = context.getBean(MessageService.class);messageService.getMessage();MessageService messageService2 = context.getBean(MessageService.class);messageService2.getMessage(); //返回结果return "result";});CompletableFuture<String> task2 = CompletableFuture.supplyAsync(()->{ //模拟执行耗时任务MessageService messageService = context.getBean(MessageService.class);messageService.getMessage();MessageService messageService2 = context.getBean(MessageService.class);messageService2.getMessage(); //返回结果return "result";});task1.get();task2.get();}}
观察输出结果;
Not exists messageServiceImpl; hashCode: 1057328090Not exists messageServiceImpl; hashCode: 784932540Exists messageServiceImpl; hashCode: 1057328090Exists messageServiceImpl; hashCode: 784932540
上述结果验证ThreadScope“相同的线程相同名字的bean是同一个对象;不同的线程的相同名字的bean是不同的对象”
源码
见https://github.com/waylau/spring-5-book 的 s5-ch02-custom-scope-annotation
项目。
参考引用
《Spring 5 开发大全》:
转载于:https://blog.51cto.com/13689432/2361929
如何在 Spring 中自定义 scope相关推荐
- java spring scope_如何在Spring中自定义scope的方法示例
大家对于 Spring 的 scope 应该都不会默认.所谓 scope,字面理解就是"作用域"."范围",如果一个 bean 的 scope 配置为 sing ...
- error:lnk2005 已经在*.obj中定义_如何在 Spring 中自定义 scope
大家对于 Spring 的 scope 应该都不会默认.所谓 scope,字面理解就是"作用域"."范围",如果一个 bean 的 scope 配置为 sing ...
- java 自定义xml_6.1 如何在spring中自定义xml标签
dubbo自定义了很多xml标签,例如,那么这些自定义标签是怎么与spring结合起来的呢?我们先看一个简单的例子. 一 编写模型类 1 packagecom.hulk.testdubbo.model ...
- 如何在spring中读取properties配置文件里面的信息
如何在spring中读取properties配置文件里面的信息 <!-- 正文开始 --> 一般来说.我们会将一些配置的信息放在.properties文件中. 然后使用${}将配置文件中的 ...
- spring中自定义注解(annotation)与AOP中获取注解___使用aspectj的@Around注解实现用户操作和操作结果日志
spring中自定义注解(annotation)与AOP中获取注解 一.自定义注解(annotation) 自定义注解的作用:在反射中获取注解,以取得注解修饰的类.方法或属性的相关解释. packag ...
- SPRING中属性SCOPE的prototype是什么意思
SPRING中属性SCOPE的prototype是什么意思 关键字: spring中属性scope的prototype是什么意思 默认情况下,从bean工厂所取得的实例为Singleton(bean的 ...
- 如何在Unity中自定义光源,包含URP管线和Build in 管线(一)
如何在Unity中自定义光源,包含URP管线和Build in 管线(一) 众所周知,光照在游戏画面效果上占了很大比例,一个游戏画面好不好,用最简单的理解来说,就是看游戏画面亮不亮,当然这个亮不是不是 ...
- day05 Spring中自定义注解的用处-之获取自定义的Servie
PS: 在RPC远程调用中,想要获取自定义的service的方法,就得自定义标签遍历拿到方法 PS:在spring中,两个最核心的 概念是aop和ioc,aop其实就是动态代理. ioc 就是解决对象 ...
- eclipse中自定义视图_如何在Windows中自定义文件夹视图设置
eclipse中自定义视图 While the Windows File Explorer seems somewhat simplified compared to older versions, ...
最新文章
- Epoch不仅过时,而且有害?Reddit机器学习板块展开讨论
- PHP获取当前时间戳,当前时间、及解决时区问题
- 大连网络推广浅析网站如何实现加快收录的方法?
- Linux命令之ln软链接
- 有一整片蓝天 停住时间。
- 2008年最新的100条经典句子
- 异常处理准则和最佳实践
- zabbix的rc控制脚本
- aes密文长度_RSA加密密文可变(一句话说明)
- Android权限Uri.parse总结
- Unity时钟定时器插件
- Python多线程笔记——简单函数版和类实现版
- 雷达的正交波形设计matlab源码,雷达系统设计MATLAB仿真
- pythonmatplotlib绘图小提琴_使用seaborn制图(小提琴图)
- python 在window 系统 连接并操作远程 oracle 数据库
- 开源下载 | 基于Scikit-learn、Keras和TensorFlow的机器学习实战
- 亚马逊alexa智能家电_如何使用Amazon Alexa轻松设置智能家居设备
- 贾俊平统计学思维导图- 第十三章 时间序列分析和预测
- 疲劳检测(Fatigue Detection Algorithm)
- 4-20mA电流光纤中继器的原理和应用
热门文章
- JAVA day02 流程控制语句
- 帧栈使用的基本用法c语言,栈帧详解
- sap客户主数据bapi_【SD系列】SAP SD模块-创建供应商主数据BAPI
- html静态页面跳转传值,在静态页面html中跳转传值
- sun.java2d.fontpath,java起用默认浏览器
- zoom怎么解除静音_如何召开一场Zoom视频会议
- 在主线程执行_深入理解JavaScript执行机制
- 250分b区计算机专硕,2021兰州大学研究生复试分数线
- java 查找引用_java – Eclipse查找方法的引用
- android 平板横版布局,引领构建安卓平板横屏生态 华为MatePad Pro凭什么?