5

I have a question about spring mvc and thread safety.

We are developing web application that will be stored on tomcat. If I understand right, Tomcat creates thread per request and it has some thread pool. Now, dispatcher servlet is shared between requests and probably is thread safe.

But when I create controller like this:

@Controller
@RequestMapping("/manage")
public class QuestionManagementController {

he has Singleton scope so the same controller is used by every request that comes from every user.

I am wondering how this problem is usually solved:

1: are controllers created with Session scope? (but I think that there also could be problems if one user quickly do some things that may lead to race conditions in the controller).

2: controllers are scoped as request

3: creating stateless controllers that don't share any variables at class level, or have them in read only mode

or maybe there is some better "best practice" that solves this kind of problem.

I am asking this question, because now we have them as Singleton scoped, and there is a problem, that in most methods we are querying for user in the database , and we can't store this information in class level variable because of the scope. We could try to use some thread safe collection but later there could be other resources needing synchronized access.

Cratylus
  • 52,998
  • 69
  • 209
  • 339
Andna
  • 6,539
  • 13
  • 71
  • 120
  • You can't store *what* information in an instance variable? – Dave Newton Aug 25 '12 at 13:25
  • The object of class `User`, if there would be multiple user requests I would need to store this information is some kind of syncrhonized collection if the scope of controllers will be singletons. That is why I am wondering if maybe they should be made as `Session` scoped. – Andna Aug 25 '12 at 13:28
  • 1
    Why do you need to store that in the controller? Can't you get it from the session? – Dave Newton Aug 25 '12 at 13:29
  • You mean, store this information with session object? – Andna Aug 25 '12 at 13:30
  • Where else would session-level user information go? – Dave Newton Aug 25 '12 at 13:31
  • Right, that is reasonable. I will just do that. Last question, the same that I asked @RC, it is then common practice to create Spring controllers as singletons? – Andna Aug 25 '12 at 13:34
  • AFAIK it's very common; there's rarely a reason not to. That said, per-request controllers exist in other frameworks (Struts 2, Rails, don't know what else). – Dave Newton Aug 25 '12 at 13:41

1 Answers1

5

A lot of parameters can be added to the controller methods like request, session, principal etc to avoid your problem.

Usually there's a 3-layers architecture:

  • @Controller (they delegates to services)
  • @Service (they do the work using DAOs or Repositories)
  • @Repository (or DAOs, they do DB access)

So depending on what you are querying for in the DB, I would advise having a service, injected by Spring with a cache if hitting the DB is costly and everytime you need the stuff from the DB call the service (i.e. nothing stored in the controller class level)

A short example, let's say we are behind spring-security and everything need a fully logged-in user. We have an userData table where the key is the user login, we have an url /data to get a page showing my user data:

@Controller
@RequestMapping("/data")
public class UserDataController
{
    @Autowired
    private UserService userService;

    @RequestMapping(value = "", method = RequestMethod.GET)
    public ModelAndView data(final Principal principal) {
        Assert.notNull(principal); // throw if assertion fails
        Assert.hasText(principal.getName());

        final UserData userData = this.userService.findByUserName(principal.getName());
        Assert.notNull(userData, "userData not found");

        return new ModelAndView("userData", "userData", userData);
    }
}

@Service("userService")
public class userService 
{
    private static final String USERDATA_CACHE = "com.acme.foo.UserData";

    @Autowired
    private UserDataRepository userDataRepository;

    @Cacheable(USERDATA_CACHE)
    public UserData findByUserName(final String userName) {
        return this.userDataRepository.findByUserName(userName);
    }
}

@Repository
public class UserDataRepository
{
    // or use spring-data-jpa

    public UserData findByUserName(final String userName) {
        // query table userData and create an UserData from results.
    }
}

Here I use principal and spring ensure that this is current user one.

Refs:

Note sure if this answer fully to your concerns

Community
  • 1
  • 1
  • Partially, `@Cacheable` is interesting, I will look into that, but is it common practice to make controllers singletons? – Andna Aug 25 '12 at 13:30
  • Link "lot of parameters can be added" is dead. – Raedwald May 28 '13 at 12:12
  • Although your answer has been accepted as correct, I don't see how it answers the OPs question. Those numerous parameters do not indicate whether controllers must be thread-safe. – Raedwald May 28 '13 at 14:50
  • If managed by spring (ie. `@Controller`) it's singleton (if you rely on spring for getting a controller instance and this controller is not in the prototype scope, cf. spring docs) –  May 28 '13 at 15:59