简体   繁体   中英

In SpringMVC Controller layer, @Scope(“prototype”) vs @Scope(“singleton”)

I have the following Controller code using 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";
    }
}

When use @Scope("prototype") , each request comes, the output of this.hashCode() are different, meaning that when each request comes, a new MessageController instance will be created.

If not use @Scope("prototype") , default will be @Scope("singleton") , each request comes, the output of this.hashCode() are same, meaning only one MessageController instance is created.

I'm not sure when should use @Scope("prototype") , when not?

Let's say you code like a pig and do something like that in your controller:

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

Your controller would become stateful: it has a state ( allMessages ) that can't be shared between two requests. The controller is not thread-safe anymore. If it was called concurrently to handle two concurrent requests, there might be a race condition.

You could avoid this problem by making the controller a prototype: each request would be handled by a separate controller.

Or you could do the right thing and make the code stateless, in which case creating a new controller for each request would be useless, since the controller would be stateless and thus thread-safe. The scope could then keep its default value: 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;
}

If you use singleton you must ensure that you do not hold state in your controller or that any state you do hold is designed to be shared between invocations. Usually business service components are built this way and are safe to inject into a singleton controller.

There are also other scopes you can consider depending on your spring configurations and libraries.

You can make beans request and session scoped.

I would tend to make a controller class request scoped rather than prototype scoped as this would ensure that multiple uses of the controller from a single request are getting the same object.

You can use session scope if you want to hold state that is kept across multiple requests in a session. But spring has other ways of achieving the same thing with @SessionAttributes

Finally you can use injection of a java.inject.Provider into a singleton bean using ProviderCreatingFactoryBean.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM