在 SpringMVC 控制器层中,@Scope( "prototype" ) 与 @Scope( "singleton" )



我使用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将其注入到单身豆中。

最新更新