大家对于 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相关推荐

  1. java spring scope_如何在Spring中自定义scope的方法示例

    大家对于 Spring 的 scope 应该都不会默认.所谓 scope,字面理解就是"作用域"."范围",如果一个 bean 的 scope 配置为 sing ...

  2. error:lnk2005 已经在*.obj中定义_如何在 Spring 中自定义 scope

    大家对于 Spring 的 scope 应该都不会默认.所谓 scope,字面理解就是"作用域"."范围",如果一个 bean 的 scope 配置为 sing ...

  3. java 自定义xml_6.1 如何在spring中自定义xml标签

    dubbo自定义了很多xml标签,例如,那么这些自定义标签是怎么与spring结合起来的呢?我们先看一个简单的例子. 一 编写模型类 1 packagecom.hulk.testdubbo.model ...

  4. 如何在spring中读取properties配置文件里面的信息

    如何在spring中读取properties配置文件里面的信息 <!-- 正文开始 --> 一般来说.我们会将一些配置的信息放在.properties文件中. 然后使用${}将配置文件中的 ...

  5. spring中自定义注解(annotation)与AOP中获取注解___使用aspectj的@Around注解实现用户操作和操作结果日志

    spring中自定义注解(annotation)与AOP中获取注解 一.自定义注解(annotation) 自定义注解的作用:在反射中获取注解,以取得注解修饰的类.方法或属性的相关解释. packag ...

  6. SPRING中属性SCOPE的prototype是什么意思

    SPRING中属性SCOPE的prototype是什么意思 关键字: spring中属性scope的prototype是什么意思 默认情况下,从bean工厂所取得的实例为Singleton(bean的 ...

  7. 如何在Unity中自定义光源,包含URP管线和Build in 管线(一)

    如何在Unity中自定义光源,包含URP管线和Build in 管线(一) 众所周知,光照在游戏画面效果上占了很大比例,一个游戏画面好不好,用最简单的理解来说,就是看游戏画面亮不亮,当然这个亮不是不是 ...

  8. day05 Spring中自定义注解的用处-之获取自定义的Servie

    PS: 在RPC远程调用中,想要获取自定义的service的方法,就得自定义标签遍历拿到方法 PS:在spring中,两个最核心的 概念是aop和ioc,aop其实就是动态代理. ioc 就是解决对象 ...

  9. eclipse中自定义视图_如何在Windows中自定义文件夹视图设置

    eclipse中自定义视图 While the Windows File Explorer seems somewhat simplified compared to older versions, ...

最新文章

  1. Epoch不仅过时,而且有害?Reddit机器学习板块展开讨论
  2. PHP获取当前时间戳,当前时间、及解决时区问题
  3. 大连网络推广浅析网站如何实现加快收录的方法?
  4. Linux命令之ln软链接
  5. 有一整片蓝天 停住时间。
  6. 2008年最新的100条经典句子
  7. 异常处理准则和最佳实践
  8. zabbix的rc控制脚本
  9. aes密文长度_RSA加密密文可变(一句话说明)
  10. Android权限Uri.parse总结
  11. Unity时钟定时器插件
  12. Python多线程笔记——简单函数版和类实现版
  13. 雷达的正交波形设计matlab源码,雷达系统设计MATLAB仿真
  14. pythonmatplotlib绘图小提琴_使用seaborn制图(小提琴图)
  15. python 在window 系统 连接并操作远程 oracle 数据库
  16. 开源下载 | 基于Scikit-learn、Keras和TensorFlow的机器学习实战
  17. 亚马逊alexa智能家电_如何使用Amazon Alexa轻松设置智能家居设备
  18. 贾俊平统计学思维导图- 第十三章 时间序列分析和预测
  19. 疲劳检测(Fatigue Detection Algorithm)
  20. 4-20mA电流光纤中继器的原理和应用

热门文章

  1. JAVA day02 流程控制语句
  2. 帧栈使用的基本用法c语言,栈帧详解
  3. sap客户主数据bapi_【SD系列】SAP SD模块-创建供应商主数据BAPI
  4. html静态页面跳转传值,在静态页面html中跳转传值
  5. sun.java2d.fontpath,java起用默认浏览器
  6. zoom怎么解除静音_如何召开一场Zoom视频会议
  7. 在主线程执行_深入理解JavaScript执行机制
  8. 250分b区计算机专硕,2021兰州大学研究生复试分数线
  9. java 查找引用_java – Eclipse查找方法的引用
  10. android 平板横版布局,引领构建安卓平板横屏生态 华为MatePad Pro凭什么?