Spring中的scope配置和@scope注解

简介

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
@scope(“prototype”)

分类

  1. singleton:单例模式,在整个Spring IoC容器中,使用singleton定义的Bean将只有一个实例

  2. prototype:原型模式,每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例

  3. request:对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,即每次HTTP请求将会产生不同的Bean实例。只有在Web应用中使用Spring时,该作用域才有效

  4. session:对于每次HTTP Session,使用session定义的Bean都将产生一个新实例。同样只有在Web应用中使用Spring时,该作用域才有效

  5. globalsession:每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效

其中比较常用的是singletonprototype两种作用域。对于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
    @Component
    public class Order {
    private String orderNum = "test";

    public String getOrderNum() {
    return orderNum;
    }

    public void setOrderNum(String orderNum) {
    this.orderNum = orderNum;
    }

    @Override
    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
    @Controller
    @RequestMapping(value = "/api")
    public class MainController {
    @Autowired
    private Order order;

    private String name;

    @RequestMapping(value = "/users/{username}",method = RequestMethod.GET)
    @ResponseBody
    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
10
31name: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
    @Component
    public class Order {
    private String orderNum = "test";

    public String getOrderNum() {
    return orderNum;
    }

    public void setOrderNum(String orderNum) {
    this.orderNum = orderNum;
    }

    @Override
    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
    @Controller
    @RequestMapping(value = "/api")
    @Scope("prototype")
    public class MainController {
    @Autowired
    private Order order;

    private String name;

    @RequestMapping(value = "/users/{username}",method = RequestMethod.GET)
    @ResponseBody
    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
23name: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
    @Component
    @Scope("prototype")
    public class Order {
    private String orderNum = "test";

    public String getOrderNum() {
    return orderNum;
    }

    public void setOrderNum(String orderNum) {
    this.orderNum = orderNum;
    }

    @Override
    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
    @Controller
    @RequestMapping(value = "/api")
    public class MainController {
    @Autowired
    private Order order;

    private String name;

    @RequestMapping(value = "/users/{username}",method = RequestMethod.GET)
    @ResponseBody
    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
23name: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
    @Component
    @Scope("prototype")
    public class Order {
    private String orderNum = "test";

    public String getOrderNum() {
    return orderNum;
    }

    public void setOrderNum(String orderNum) {
    this.orderNum = orderNum;
    }

    @Override
    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
    @Controller
    @RequestMapping(value = "/api")
    @Scope("prototype")
    public class MainController {
    @Autowired
    private Order order;

    private String name;

    @RequestMapping(value = "/users/{username}",method = RequestMethod.GET)
    @ResponseBody
    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
30name: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请求的就完全分开了

参考