【问题标题】:In SpringMVC Controller layer, @Scope("prototype") vs @Scope("singleton")在 Spring MVC 控制器层中,@Scope("prototype") 与 @Scope("singleton")
【发布时间】:2015-07-20 19:14:47
【问题描述】:

我有以下使用 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"),什么时候不应该?

【问题讨论】:

    标签: java spring spring-mvc


    【解决方案1】:

    假设您在控制器中执行以下操作:

    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) 无法在两个请求之间共享。控制器不再是线程安全的。如果同时调用它来处理两个并发请求,则可能存在竞争条件。

    您可以通过将控制器设为原型来避免此问题:每个请求都将由单独的控制器处理。

    或者您可以做正确的事情并使代码无状态,在这种情况下,为每个请求创建一个新控制器将是无用的,因为控制器将是无状态的,因此是线程安全的。然后作用域可以保持其默认值:单例。

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

    【讨论】:

      【解决方案2】:

      如果您使用单例,则必须确保您不在控制器中持有状态,或者您持有的任何状态都旨在在调用之间共享。通常业务服务组件都是以这种方式构建的,并且可以安全地注入到单例控制器中。

      根据您的弹簧配置和库,您还可以考虑其他范围。

      您可以使 bean 请求和会话范围。

      我倾向于将控制器类请求设置为范围而不是原型范围,因为这将确保从单个请求中对控制器的多次使用获得相同的对象。

      如果您想在会话中保持跨多个请求的状态,您可以使用会话范围。但是 spring 有其他方法可以通过 @SessionAttributes 实现相同的目标

      最后,您可以使用 ProviderCreatingFactoryBean 将 java.inject.Provider 注入单例 bean。

      【讨论】:

        猜你喜欢
        • 2018-11-21
        • 1970-01-01
        • 2021-12-30
        • 2017-05-02
        • 1970-01-01
        • 2015-08-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多