简介
Scope,也称作用域,在Spring IOC容器是指其创建的Bean对象相对于其他Bean 对象的请求可见范围。在Spring IOC容器中具有以下几种作用域:基本作用域(singleton、prototype),Web作用域(reqeust、session、globalsession),自定义作用域。
配置
1、Spring 的作用域在装配 Bean 时就必须在配置文件中指明,配置方式如下(以 xml 配置文件为例):1
2<!-- 具体的作用域需要在 scope 属性中定义 -->
<bean id="XXX" class="com.XXX.XXXXX" scope="XXXX" />
2、基于注解开发时,@scope完成bean的作用域配置默认是单例模式(singleton)如果需要设置的话可以修改对应值与以上提到的一致例如:1
(“prototype”)
分类
singleton:单例模式,在整个Spring IoC容器中,使用singleton定义的Bean将只有一个实例
prototype:原型模式,每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例
request:对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,即每次HTTP请求将会产生不同的Bean实例。只有在Web应用中使用Spring时,该作用域才有效
session:对于每次HTTP Session,使用session定义的Bean都将产生一个新实例。同样只有在Web应用中使用Spring时,该作用域才有效
globalsession:每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效
其中比较常用的是singleton和prototype两种作用域。对于singleton作用域的Bean,每次请求该Bean都将获得相同的实例。容器负责跟踪Bean实例的状态,负责维护Bean实例的生命周期行为;如果一个Bean被设置成prototype作用域,程序每次请求该id的Bean,Spring都会新建一个Bean实例,然后返回给程序。在这种情况下,Spring容器仅仅使用new 关键字创建Bean实例,一旦创建成功,容器不在跟踪实例,也不会维护Bean实例的状态。
如果不指定Bean的作用域,Spring默认使用singleton作用域。Java在创建Java实例时,需要进行内存申请;销毁实例时,需要完成垃圾回收,这些工作都会导致系统开销的增加。因此,prototype作用域Bean的创建、销毁代价比较大。而singleton作用域的Bean实例一旦创建成功,可以重复使用。因此,除非必要,否则尽量避免将Bean被设置成prototype作用域。
示例
Spring_@Scope(“prototype”)
这里新建两个bean实例,Order和MainController,其中MainController引用了Order,主要演示了这两个类分别为单例和非单例下结果。
model和Controller都是单例
model
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Order {
private String orderNum = "test";
public String getOrderNum() {
return orderNum;
}
public void setOrderNum(String orderNum) {
this.orderNum = orderNum;
}
public String toString() {
return "Order{" +
"orderNum='" + orderNum + '\'' +
'}';
}
}MainController
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
"/api") (value =
public class MainController {
private Order order;
private String name;
"/users/{username}",method = RequestMethod.GET) (value =
public String userProfile(@PathVariable("username") String username) {
name = username;
order.setOrderNum( name);
try {
for(int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getId() + "name:" + name + "--order:" + order.getOrderNum());
Thread.sleep(2000);
}
} catch (Exception e) {
}
return order.toString();
}
}
启动之后,我们分别发送两个请求:
http://localhost:8080/api/users/aaa
http://localhost:8080/api/users/bbb
结果如下:1
2
3
4
5
6
7
8
9
1031name:aaa--order:aaa
31name:aaa--order:aaa
32name:bbb--order:bbb
31name:bbb--order:bbb
32name:bbb--order:bbb
31name:bbb--order:bbb
32name:bbb--order:bbb
31name:bbb--order:bbb
32name:bbb--order:bbb
31name:bbb--order:bbb
可以看到当第二个请求发送后,31线程里的name和order都发生了改变,因为当前的Order和Controller都是单例
model是单例,Controller不是单例
model
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Order {
private String orderNum = "test";
public String getOrderNum() {
return orderNum;
}
public void setOrderNum(String orderNum) {
this.orderNum = orderNum;
}
public String toString() {
return "Order{" +
"orderNum='" + orderNum + '\'' +
'}';
}
}Controller,注意@Scope(“prototype”)加的位置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
"/api") (value =
"prototype") (
public class MainController {
private Order order;
private String name;
"/users/{username}",method = RequestMethod.GET) (value =
public String userProfile(@PathVariable("username") String username) {
name = username;
order.setOrderNum( name);
try {
for(int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getId() + "name:" + name + "--order:" + order.getOrderNum());
Thread.sleep(2000);
}
} catch (Exception e) {
}
return order.toString();
}
}
启动之后,我们分别发送两个请求:
http://localhost:8080/api/users/aaa
http://localhost:8080/api/users/bbb
结果如下:1
2
3
4
5
6
7
8
923name:aaa--order:aaa
23name:aaa--order:aaa
24name:bbb--order:bbb
23name:aaa--order:bbb
24name:bbb--order:bbb
23name:aaa--order:bbb
24name:bbb--order:bbb
23name:aaa--order:bbb
24name:bbb--order:bbb
可以看到第二个请求发送后,23线程中的name还是aaa没有发生变化,而order受到了影响,发生了变化,因为Controller是一个新的,所以name也是一个新的变量,但是order是单例的。
model不是单例,Controller是单例
model
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
"prototype") (
public class Order {
private String orderNum = "test";
public String getOrderNum() {
return orderNum;
}
public void setOrderNum(String orderNum) {
this.orderNum = orderNum;
}
public String toString() {
return "Order{" +
"orderNum='" + orderNum + '\'' +
'}';
}
}Controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
"/api") (value =
public class MainController {
private Order order;
private String name;
"/users/{username}",method = RequestMethod.GET) (value =
public String userProfile(@PathVariable("username") String username) {
name = username;
order.setOrderNum( name);
try {
for(int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getId() + "name:" + name + "--order:" + order.getOrderNum());
Thread.sleep(2000);
}
} catch (Exception e) {
}
return order.toString();
}
}
启动之后,我们分别发送两个请求:
http://localhost:8080/api/users/aaa
http://localhost:8080/api/users/bbb
结果如下:1
2
3
4
5
6
723name:aaa--order:aaa
23name:aaa--order:aaa
24name:bbb--order:bbb
23name:bbb--order:bbb
24name:bbb--order:bbb
23name:bbb--order:bbb
24name:bbb--order:bbb
可以看到name和order都变了,可见只设置Model是没有用的
model和Controller都不是单例
model
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
"prototype") (
public class Order {
private String orderNum = "test";
public String getOrderNum() {
return orderNum;
}
public void setOrderNum(String orderNum) {
this.orderNum = orderNum;
}
public String toString() {
return "Order{" +
"orderNum='" + orderNum + '\'' +
'}';
}
}Controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
"/api") (value =
"prototype") (
public class MainController {
private Order order;
private String name;
"/users/{username}",method = RequestMethod.GET) (value =
public String userProfile(@PathVariable("username") String username) {
name = username;
order.setOrderNum( name);
try {
for(int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getId() + "name:" + name + "--order:" + order.getOrderNum());
Thread.sleep(2000);
}
} catch (Exception e) {
}
return order.toString();
}
}
启动之后,我们分别发送两个请求:
http://localhost:8080/api/users/aaa
http://localhost:8080/api/users/bbb
结果如下:1
2
3
4
5
6
7
830name:aaa--order:aaa
30name:aaa--order:aaa
30name:aaa--order:aaa
31name:bbb--order:bbb
30name:aaa--order:aaa
31name:bbb--order:bbb
30name:aaa--order:aaa
31name:bbb--order:bbb
这样30请求的和31请求的就完全分开了