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;
}
}