大家对于 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的行为:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
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();
} |
一般来说,只需要重新 get 和 remove 方法即可。
自定义线程范围内的scope
现在进入实战环节。我们要自定义一个spring没有的scope,该scope将bean的作用范围限制在了线程内。即,相同线程内的bean是同个对象,跨线程则是不同的对象。
1. 定义scope
要自定义一个spring的scope,只需实现 org.springframework.beans.factory.config.scope接口。代码如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
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测试数据" 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用于做线程之间的数据隔离。换言之,threadloacal实现了相同的线程相同名字的bean是同一个对象;不同的线程的相同名字的bean是不同的对象。
同时,我们将对象的hashcode打印了出来。如果他们是相同的对象,则hashcode是相同的。
2. 注册scope
定义一个appconfig配置类,将自定义的scope注册到容器中去。代码如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
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测试数据ponentscan; import org.springframework.context.annotation.configuration;
/** * app config. * * @since 1.0.0 2019年2月13日 * @author <a href="https://waylau测试数据" 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的名称。
3. 使用scope
接下来就根据一般的scope的用法,来使用自定义的scope了。代码如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
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测试数据" 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的名称。
4. 定义应用入口
定义spring应用入口:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
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测试数据" 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();
}
} |
运行应用观察控制台输出如下:
not exists messageserviceimpl; hashcode: 2146338580
exists messageserviceimpl; hashcode: 2146338580
输出的结果也就验证了threadscope[相同的线程相同名字的bean是同一个对象]。
如果想继续验证threadscope[不同的线程的相同名字的bean是不同的对象],则只需要将application改造为多线程即可。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
package com.waylau.spring.scope;
import java.util.concurrent测试数据pletablefuture; 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测试数据" 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(()->{ //模拟执行耗时任务 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: 1057328090
not exists messageserviceimpl; hashcode: 784932540
exists messageserviceimpl; hashcode: 1057328090
exists messageserviceimpl; hashcode: 784932540
上述结果验证threadscope[相同的线程相同名字的bean是同一个对象;不同的线程的相同名字的bean是不同的对象]
源码
见 的 s5-ch02-custom-scope-annotation项目。
以 https://github测试数据/waylau/spring-5-book 上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
原文链接:https://my.oschina.net/waylau/blog/3009991
查看更多关于如何在Spring中自定义scope的方法示例的详细内容...