Issue
I have a Spring 5
based webapp where the controller
calls this service
class and gets the current logged in User
information.
Since loggedInUser
object has been defined as an instance variable here, will this create a race condition when multiple users try to log into the application and access pages with their credentials ?
Should this be a local variable instead of instance variable within the getLoggedInUser()
method ?
I was under the impression that Tomcat
will create a new thread for every HTTP
request and a brand new
instance of UserService
bean will be created every time a request comes in, irrespective of the user accessing it.
And how can I identify if the bean used by Spring
is new
instance or singleton
? Is there a way to check this at runtime using System.out.println()
or any other methodology/tool?
So,
User 1 -> new UserController() -> new UserService() -> welcome.jsp
User 1 -> new ProductController() -> new ProductService() -> new UserService() -> product.jsp
User 2 -> new UserController() -> new UserService() -> welcome.jsp
User 2 -> new ProductController() -> new ProductService() -> new UserService() -> product.jsp
UserService.java
@Service
public class UserService {
private User loggedInUser;
public User getLoggedInUser(HttpServletRequest request) {
if (this.loggedInUser != null) {
return this.loggedInUser;
}
this.loggedInUser = (User) session.getAttribute("loggedInUser");
return loggedInUser;
}
}
Solution
The default scope for Spring beans is singleton
Spring Documentation. This means that only one instance of a bean will be created. This can be demonstrated by the following snippet of code.
@RestController
public class TestController {
@GetMapping("/test")
public void test() {
System.out.println(this);
}
}
Every time /test
endpoint is called, the output will be the same, something like this:
com.example.TestController@17870d99
com.example.TestController@17870d99
com.example.TestController@17870d99
Using a different scope, (request
scope, for example), running the following code:
@RestController
@Scope(value = WebApplicationContext.SCOPE_SESSION)
public class TestController {
@GetMapping("/test")
public void test() {
System.out.println(this);
}
}
we will get the following output:
com.example.TestController@3213427f
com.example.TestController@2318434f
com.example.TestController@5e0df012
We can see that the controller is instantiated with every session (in our case with every call to /test
end-point).
Now, it is not recommended to use an instance variable in a singleton bean, since this will have unintended consequences (like race conditions, or leaking unauthorized data). If we would want to use an instance variable, we should use the appropriate scope for the bean.
Or just avoid instance variables at all.
Answered By - Ervin Szilagyi
Answer Checked By - Willingham (JavaFixing Volunteer)