Reputation: 6689
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.
Upvotes: 5
Views: 6788
Reputation:
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
Upvotes: 5