四种常见的scope属性
singleton scope
在spring容器中,当一个bean被声明成singleton时,容器初始化的时候就会生成一个实例,所有请求调用指向的都是同一个实例,这也是spring中bean定义的默认属性
prototype scope
与singleton属性完全不同,每次引用都会导致一个新的实例的创建,和new操作很像
request scope
每一次http请求都会创建一个实例,当http请求生命周期结束的时候,实例自动销毁
session scope
与http请求的不同的是,在每一次http请求的session生命周期内只创建一个实例,在http请求session生命周期(浏览器默认一次session回话的生命周期为30min)结束的时候,实例自动销毁
不同scope的线程安全性
这应该是大家在都考虑过的一个问题,用一个简单的例子来说明
@Controller
@ResponseMapping(value = "/Test")
@Scope("singleton") /**这里根据要求,切换不同scope属性 **/
public class TestScope {
private int i;
@ResponseMapping("scopeInt")
public String value(){
i++;
System.out.println(i);
return "index";
}
}
不同scope下的结果分析
singleton
当bean属性定义为singleton时,我们在浏览器上每一次发送请求的结果都会受到上一次的影响,在同一台电脑上用不同的浏览器如此,用不同的电脑发送亦如此!这也就是说,如果不同的用户调用这个bean实例的时候会相互影响,这将是一件非常恐怖的事情。
prototype
每次请求的结果都是1,因为每次调用都会创建一个实例,各自互不影响,所以没有发生上面那种串数据的情况
request
和prototype的情况一样,每次结果都是1,每个request请求都会导致spring容器内创建一个实例,各实例互不影响。
session
这个比较有意思,在同一个浏览器下,每次请求的结果会都彼此影响。在同一台电脑下,使用不同浏览器请求,请求结果彼此独立,即使用浏览器2发送请求的结果不会受到浏览器1请求的影响,也就是说在浏览器的session生命周期内spring容器中只创建了一个实例,session的生命周期结束,容器内的实例也随之销毁
request 和 prototype的区别
上面的结果中,bean在request属性下和prototype属性下产生的结果一样,而且原理似乎也相同,每次请求创建一个实例,但是他们还是有着很大的区别:
1.应用场景不一样:request请求下创建bean需要对http请求进行拦截监听,http请求发生,实例创建,http请求结束,实例销毁。prototype模式则不需要对http请求进行拦截,任何调用都会创建一个新的实例
2.生命周期不一样:request模式下的bean实例生命周期很短,当次request请求的生命周期就是bean实例的生命周期,容器中的bean实例对象会伴随着request请求的生命周期结束而销毁。prototype的生命周期不完全受到spring容器的管理,也就是说,当对bean实例的调用结束之后它还是会驻留在容器中,如果需要清除prototype模式下的bean实例和它所持有的资源,需要额外的代码来做相应处理。
singleton模式下要注意的问题
前面已经说过,在spring中,bean的默认属性是singleton,而在singleton模式下,spring并不保证线程安全,所以将bean声明为singleton时一定要注意线程安全性问题。
在上面的例子程序中,之所有会出现不同的请求间会发生串数据的情况,主要是因为定义了bean的状态(state),也就是给class定义了一个成员变量 int i。在singleton模式下,bean不能定义为有状态,因为每个线程调用的是同一个实例,有可能会修改实例的状态。
针对这种情况有两种解决方案:
1.将变量定义在方法中。线程可以理解为一系列方法的有序执行,而每个线程都有自己独立的栈帧空间,方法中的变量在线程栈内局部有效
2.用ThreadLocal类将私有成员变量进行包装。ThreadLocal类会为每个线程创建一个变量副本