声明
文章参考:https://blog.51cto.com/lavasoft/1394669
https://blog.csdn.net/qq_27026603/article/details/67953879
https://www.cnblogs.com/nangonghui/p/8988647.html
正文
Spring MVC Controller默认是单例的:
单例的原因有二:
1、为了性能。
2、不需要多例。
1、这个不用废话了,单例不用每次都new,当然快了。
2、不需要实例会让很多人迷惑,因为spring mvc官方也没明确说不可以多例。
我这里说不需要的原因是看开发者怎么用了,如果你给controller中定义很多的属性,那么单例肯定会出现竞争访问了。
因此,只要controller中不定义属性,那么单例完全是安全的。下面给个例子说明下:
package com.xgcd.scope; import org.springframework.context.annotation.Scope; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController //@Scope(value = "prototype") @RequestMapping("/mvcController") public class SpringMvcControllerScope { private static int st = 0;// 静态的 private int index = 0;// 非静态的 @RequestMapping("/test") public String test() { System.out.println(st++ + "|" + index++); return "index"; } }
1/当scope="prototype"时, 原型模式, 多例, 多次请求(集成了oauth2)
http://localhost:8080/mvcController/test?access_token=9f041ad8-d146-455d-a7ec-7a473f6ae463
控制台结果:
0|0
1|0
2|0
3|0
4|0
5|0
6|0
...
2/把@Scope注解注释掉, 则默认是单例, 多次请求
http://localhost:8080/mvcController/test?access_token=4a0e8345-dd38-4d7a-82f2-5681299e8f07
控制台结果:
0|0
1|1
2|2
3|3
4|4
5|5
6|6
...
由此可见,单例是不安全的,会导致属性重复使用。
最佳实践:
1、不要在controller中定义成员变量。
2、万一必须要定义一个非静态成员变量时候,则通过注解@Scope("prototype"),将其设置为多例模式。
扩展
当面试官问springmvc和struts2的区别的时候, 也可以从这一点出发, 我想他要考察的点也在于此?
尽量不要在controller里面去定义属性,如果在特殊情况需要定义属性的时候,那么就在类上面加上注解@Scope("prototype")改为多例的模式。
以前struts是基于类的属性进行发的,定义属性可以整个类通用,所以默认是多例,不然多线程访问肯定是共用类里面的属性值的,肯定是不安全的。
但是springmvc是基于方法的开发,都是用形参接收值,一个方法结束参数就销毁了,多线程访问都会有一块内存空间产生,里面的参数也是不会共用的,所有springmvc默认使用了单例,所以controller里面不适合在类里面定义属性,只要controller中不定义属性,那么单例完全是安全的。
springmvc这样设计主要的原因也是为了提高程序的性能和以后程序的维护只针对业务的维护就行,要是struts的属性定义多了,都不知道哪个方法用了这个属性,对以后程序的维护还是很麻烦的。
如何解决单例模式下线程不安全的问题?
1、不要在controller中定义成员变量。
2、万一必须要定义一个非静态成员变量时候,则通过注解@Scope(“prototype”),将其设置为多例模式。
3、在Controller中使用ThreadLocal变量。