聲明
文章參考: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變量。