SpringMVC的Controller默認是單例的,對於同一個Controller而言,在整個web生命周期內只有一個對象。如果在Controller里寫了一個成員變量,這個變量是對所有線程可見的。
@Slf4j
@Controller
public class TestController {
private List<String> list = new ArrayList<>();
@RequestMapping(method = RequestMethod.GET, value = "/test")
public ModelAndView test(Model model) {
list.add(this.hashCode() + "");
ModelAndView modelAndView = new ModelAndView("test");
modelAndView.addObject("name", list.hashCode() + "--" +list.size());
return modelAndView;
}
}
頁面模板如下
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title></title>
</head>
<body>
Hello ${name}
</body>
</html>
前后兩次請求list的size在發生變化說明兩次請求操作的是同一個list對象。由於list在添加對象后hashCode也會發生變化,所以兩次請求后的list的hashCode也發生了變化。
如果將Controller設置為原型模式,則每次請求都會new一個Controller的對象,此時不會出現線程安全問題。
@Slf4j
@Scope("prototype")
@Controller
public class TestController {
//······
}
此時雖然可能避免上述的線程安全問題,但是jvm必須為每次請求創建新的對象,在請求完成之后銷毀對象,這必然會增加系統的性能開銷。
附:ArrayList計算hashCode的源碼
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
public int hashCode() {
int hashCode = 1;
for (E e : this)
hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
return hashCode;
}
}