先看這么一段代碼:
@Service
public class AccountService {private String message;public void foo1() {if (true) {this.message = "a";} else {this.message = "b";}}public void foo2() {// 改動this.message的代碼...// ... ...}
}
假設你打算在@Controller里這么調用AccountService :?
accountService.foo1();model.addAttribute(accountService.getMessage());
那么就有線程安全的危急了。
問題原因
在Spring中。bean的默認scope是singleton,也就是說容器中僅僅有一個bean的實例。而在Java Web環境中,webserver會為每個請求創建一個線程來處理它。這樣一來。在@Controller中調用@Service bean的方法就會導致有多個線程在運行@Service方法。比如線程A在運行foo1()方法,線程B在運行foo2()方法。
那么問題來了,多個線程同一時候讀寫message成員變量。就可能讓getMessage()方法返回錯誤的值
解決方法
1. 將@Service bean的scope改為 "request",即:
@Service
@Scope("request")
public class AccountService {private String message;
這樣Spring會為每個請求分別創建一個AccoutService對象,每個線程都有自己的message變量。就不會出錯了。
但壞處是創建@Service bean的開銷往往比較大,會導致程序性能下降。
2. 使用不可變對象(Immuable Object)封裝message變量
定義例如以下類:
class MessageWrapper {private String message;public MessageWrapper(String msg) {this.message = msg;}// 僅僅提供get方法public String getMessage() {return this.message;}
}
AccountService的foo1()方法改動例如以下:
@Service
public class AccountService {public MessageWrapper foo1() {if (true) {return new MessageWrapper("a");} else {return new MessageWrapper("b");}// ... ...}
這樣便能夠完美避免線程安全問題,又不會帶來過多的額外開銷。