在SpringMVC Controller层中,@ Scope(“prototype”)vs @Scope(“singleton”)

时间:2015-05-10 05:34:48

标签: java spring spring-mvc

我使用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"),何时不使用?

2 个答案:

答案 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中。