我使用SpringMVC获得以下Controller代码:
@Controller
@Scope("prototype")
@RequestMapping("/messages")
public class MessageController {
@RequestMapping(value="/index", method=RequestMethod.GET)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public String displayAllMessages(ModelMap model) {
System.out.println(this.hashCode());
// processing
return "messages";
}
}
当使用@Scope("prototype")
时,每个请求都会出现,this.hashCode()
的输出不同,这意味着当每个请求到来时,都会创建一个新的MessageController
实例。
如果不使用@Scope("prototype")
,则默认为@Scope("singleton")
,每个请求都会出现,this.hashCode()
的输出相同,这意味着只创建了一个MessageController
实例。
我不确定何时应该使用@Scope("prototype")
,何时不使用?
答案 0 :(得分:9)
让我们说你像猪一样代码,在你的控制器中做类似的事情:
private List<String> allMessages;
public String displayAllMessages(ModelMap model) {
allMessages = new ArrayList<>();
fillMessages();
model.put(messages, allMessages);
return "messages";
}
private void fillMessages() {
allMessages.add("hello world");
}
您的控制器将变为有状态:它具有无法在两个请求之间共享的状态(allMessages
)。控制器不再是线程安全的。如果同时调用它来处理两个并发请求,则可能存在竞争条件。
您可以通过使控制器成为原型来避免此问题:每个请求都将由单独的控制器处理。
或者你可以做正确的事情并使代码无状态,在这种情况下,为每个请求创建一个新的控制器将是无用的,因为控制器将是无状态的,因此是线程安全的。然后范围可以保持其默认值:singleton。
public String displayAllMessages(ModelMap model) {
List<String> messages = fillMessages();
model.put(messages, allMessages);
return "messages";
}
private List<String> fillMessages() {
List<String> allMessages = new ArrayList<>();
allMessages.add("hello world");
return allMessages;
}
答案 1 :(得分:1)
如果使用单例,则必须确保不在控制器中保持状态,或者保留的任何状态都设计为在调用之间共享。通常,业务服务组件以这种方式构建,并且可以安全地注入单一控制器。
根据您的弹簧配置和库,还可以考虑其他范围。
您可以使bean请求和会话作用域。
我倾向于将控制器类请求作为范围而不是原型作用域,因为这将确保来自单个请求的控制器的多次使用获得相同的对象。
如果要保持会话中多个请求之间保持的状态,则可以使用会话范围。但是Spring还有其他方法可以通过@SessionAttributes实现相同的功能
最后,您可以使用ProviderCreatingFactoryBean将java.inject.Provider注入到单个bean中。