总结一下关于Spring中bean的scope属性

关于Spring bean的scope属性

  • Spring的bean默认是singleton-单例模式的,即Spring容器只存在一个共享的bean实例

  • 对于singleton-单例模式,如果有共享变量会导致线程不安全

  • 如果为prototype-原型模式,那么每次对bean的请求都会创建一个新的bean实例

  • prototype-原型模式是线程安全的,Spring中的Controller默认是singleton

  • 可以在类上面通过@Scope("prototype")设置为多例,或者在xml中设置scope="prototype"

  • singleton-单例模式相对prototype-原型模式性能更高,因为不会每次对bean的请求都会创建一个新的bean实例

  • 二者选择的原则:有状态的bean都使用prototype,而对无状态的bean则应该使用singleton

  • 有无状态是指bean中有无成员变量

  • Struts2的Action默认是多例的,原因在于Struts2将表单数据作为Action的成员变量

  • scope还可以设置成request、session和global session

举例说明Controller的singleton和prototype的区别

  • TestController如下

    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("/test")
    @Api(basePath = "/test", value = "test", description = "测试相关接口", position = 8)
    public class TestController {

    private Map<String, String> cacheData = Collections.synchronizedMap(new HashMap<String, String>());

    @ResponseBody
    @RequestMapping(value = "/test_cache", method = RequestMethod.GET, produces = "application/json; charset=utf-8")
    @ApiOperation(value = "测试scope", notes = "测试scope", response = KVBean.class, responseContainer = "List")
    public List<KVBean> getTestCache(@RequestParam String key, @RequestParam String value) throws Exception {

    System.out.println(Thread.currentThread().getName());
    cacheData.put(key, value);

    List<KVBean> data = new ArrayList<KVBean>();
    for (Entry<String, String> it : cacheData.entrySet()) {
    data.add(new KVBean(it.getKey(), it.getValue()));
    }

    return data;
    }

    }
  • 第一次在浏览器中输入
    http://localhost:8080/plainWebApi/test/test_cache?key=1&value=1
    返回
    [{"key":"1","value":"1","next":null}]
    第二次在浏览器中输入
    http://localhost:8080/plainWebApi/test/test_cache?key=2&value=2
    返回
    [{"key":"1","value":"1","next":null},{"key":"2","value":"2","next":null}]
    可见对于两次请求cacheData都是同一个对象,TestController当然也没有被new。

  • 增加@Scope(value = "prototype")的TestController如下

    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
    @Controller
    @RequestMapping("/test")
    @Api(basePath = "/test", value = "test", description = "测试相关接口", position = 8)
    @Scope(value = "prototype")
    public class TestController {

    private Map<String, String> cacheData = Collections.synchronizedMap(new HashMap<String, String>());

    @ResponseBody
    @RequestMapping(value = "/test_cache", method = RequestMethod.GET, produces = "application/json; charset=utf-8")
    @ApiOperation(value = "测试scope", notes = "测试scope", response = KVBean.class, responseContainer = "List")
    public List<KVBean> getTestCache(@RequestParam String key, @RequestParam String value) throws Exception {

    System.out.println(Thread.currentThread().getName());
    cacheData.put(key, value);

    List<KVBean> data = new ArrayList<KVBean>();
    for (Entry<String, String> it : cacheData.entrySet()) {
    data.add(new KVBean(it.getKey(), it.getValue()));
    }

    return data;
    }

    }
  • 同样第一次在浏览器中输入
    http://localhost:8080/plainWebApi/test/test_cache?key=1&value=1
    返回
    [{"key":"1","value":"1","next":null}]
    第二次在浏览器中输入
    http://localhost:8080/plainWebApi/test/test_cache?key=2&value=2
    返回
    [{"key":"2","value":"2","next":null}]
    可见对于两次请求cacheData都是不是一个对象,TestController当然也不是同一个。

例子的详细代码

参考:

Buy me a cup of coffee