我使用springmvc具有以下控制器代码:
@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")
,
假设您在控制器中做类似的事情:
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;
}
如果您使用Singleton,则必须确保您不会在控制器中保持状态,否则您所保留的任何状态均被设计为在调用之间共享。通常,业务服务组件是这样构建的,可以安全地注入单例控制器。
您还可以根据春季配置和库考虑其他范围。
您可以进行bean请求和会话范围。
我倾向于使控制器类请求范围范围而不是原型范围,因为这将确保来自单个请求的控制器的多次用途。
。如果要保持会话中保留在会话中多个请求中的状态,则可以使用会话范围。但是春天还有其他方法可以通过@SessionAttributes
来实现同样的事情。最后,您可以使用ProviderCreatingFactoryBean使用Java.Inkect.Inked.sprovider将其注入到单身豆中。