Spring摘要记录

Spring中Controller单例

  • controller默认是单例的,不要使用非静态的成员变量,否则会发生数据逻辑混乱。 正因为单例所以不是线程安全的。

我们下面来简单的验证下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.riemann.springbootdemo.controller;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class ScopeTestController {

private int num = 0;

@RequestMapping("/testScope")
public void testScope() {
System.out.println(++num);
}

@RequestMapping("/testScope2")
public void testScope2() {
System.out.println(++num);
}

}

得到的不同的值,这是线程不安全的。

接下来我们再来给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
package com.riemann.springbootdemo.controller;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@Scope("prototype")
public class ScopeTestController {

private int num = 0;

@RequestMapping("/testScope")
public void testScope() {
System.out.println(++num);
}

@RequestMapping("/testScope2")
public void testScope2() {
System.out.println(++num);
}

}

相信大家不难发现 :

单例是不安全的,会导致属性重复使用。

解决方案

  1. 不要在controller中定义成员变量。
  2. 万一必须要定义一个非静态成员变量时候,则通过注解@Scope(“prototype”),将其设置为多例模式。
  3. 在Controller中使用ThreadLocal变量

Spring MVC默认是单例模式,Controller、Service、Dao都是单例所以在使用不当存在一定的安全隐患。Controller单例模式的好处在与:

  • 提高性能,不用每次创建Controller实例,减少了对象创建和垃圾收集的时间
  • 没多例的必要,由于只有一个Controller的实例,当多个线程同时调用它的时候,它的成员变量就不是线程安全的。
    当然在大多数情况下,我们根本不需要Controller考虑线程安全的问题,除非在类中声明了成员变量。因此Spring MVC的Controller在编码时,尽量避免使用实例变量。如果一定要使用实例变量,则可以改用以下方式:
    Controller中声明 scope=”prototype”,即设置为多例模式
    在Controller中使用ThreadLocal变量,如:private ThreadLocal count = new ThreadLocal();

springmvc singleton有几种解决方法:

1、在控制器中不使用实例变量(可以使用方法参数的形式解决,参考博文 Spring Bean Scope 有状态的Bean 无状态的Bean)
2、将控制器的作用域从单例改为原型,即在spring配置文件Controller中声明 scope=“prototype”,每次都创建新的controller
3、在Controller中使用ThreadLocal变量