Spring Bean 服务 class Web 应用程序中的实例变量和线程问题
Spring Bean Service class instance variable and threading issue in a web application
我有一个基于 Spring 5
的网络应用程序,其中 controller
调用此 service
class 并获取当前登录的 User
信息。
由于 loggedInUser
对象已在此处定义为实例变量,当多个用户尝试使用他们的凭据登录应用程序并访问页面时,这会产生竞争条件吗?
这应该是 getLoggedInUser()
方法中的局部变量而不是实例变量吗?
我的印象是 Tomcat
会为每个 HTTP
请求创建一个新线程,并且每次都会创建 UserService
bean 的品牌 new
实例一个请求进来了,不管用户访问它。
我如何确定 Spring
使用的 bean 是 new
实例还是 singleton
?有没有办法在运行时使用 System.out.println()
或任何其他 methodology/tool?
检查这个
所以,
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;
}
}
Spring IoC 容器将创建 UserService
一次,如果这是默认的 Singleton
,将在使用它的地方注入依赖项。
您可以使用 VisualVM 进行堆转储并进行分析。
并将 loggedInUser
更改为 ThreadLocal
可能会有所帮助。
Spring bean 的默认范围是 singleton
Spring Documentation。这意味着将只创建一个 bean 实例。这可以通过以下代码片段来证明。
@RestController
public class TestController {
@GetMapping("/test")
public void test() {
System.out.println(this);
}
}
每次调用 /test
端点时,输出都是相同的,如下所示:
com.example.TestController@17870d99
com.example.TestController@17870d99
com.example.TestController@17870d99
使用不同的作用域,(例如request
作用域),运行以下代码:
@RestController
@Scope(value = WebApplicationContext.SCOPE_SESSION)
public class TestController {
@GetMapping("/test")
public void test() {
System.out.println(this);
}
}
我们将得到以下输出:
com.example.TestController@3213427f
com.example.TestController@2318434f
com.example.TestController@5e0df012
我们可以看到每次会话都会实例化控制器(在我们的例子中,每次调用 /test
端点)。
现在,不建议在单例 bean 中使用实例变量,因为这会产生意想不到的后果(如竞争条件,或泄露未经授权的数据)。如果我们想使用实例变量,我们应该为 bean 使用适当的范围。
或者完全避免使用实例变量。
我有一个基于 Spring 5
的网络应用程序,其中 controller
调用此 service
class 并获取当前登录的 User
信息。
由于 loggedInUser
对象已在此处定义为实例变量,当多个用户尝试使用他们的凭据登录应用程序并访问页面时,这会产生竞争条件吗?
这应该是 getLoggedInUser()
方法中的局部变量而不是实例变量吗?
我的印象是 Tomcat
会为每个 HTTP
请求创建一个新线程,并且每次都会创建 UserService
bean 的品牌 new
实例一个请求进来了,不管用户访问它。
我如何确定 Spring
使用的 bean 是 new
实例还是 singleton
?有没有办法在运行时使用 System.out.println()
或任何其他 methodology/tool?
所以,
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;
}
}
Spring IoC 容器将创建 UserService
一次,如果这是默认的 Singleton
,将在使用它的地方注入依赖项。
您可以使用 VisualVM 进行堆转储并进行分析。
并将 loggedInUser
更改为 ThreadLocal
可能会有所帮助。
Spring bean 的默认范围是 singleton
Spring Documentation。这意味着将只创建一个 bean 实例。这可以通过以下代码片段来证明。
@RestController
public class TestController {
@GetMapping("/test")
public void test() {
System.out.println(this);
}
}
每次调用 /test
端点时,输出都是相同的,如下所示:
com.example.TestController@17870d99
com.example.TestController@17870d99
com.example.TestController@17870d99
使用不同的作用域,(例如request
作用域),运行以下代码:
@RestController
@Scope(value = WebApplicationContext.SCOPE_SESSION)
public class TestController {
@GetMapping("/test")
public void test() {
System.out.println(this);
}
}
我们将得到以下输出:
com.example.TestController@3213427f
com.example.TestController@2318434f
com.example.TestController@5e0df012
我们可以看到每次会话都会实例化控制器(在我们的例子中,每次调用 /test
端点)。
现在,不建议在单例 bean 中使用实例变量,因为这会产生意想不到的后果(如竞争条件,或泄露未经授权的数据)。如果我们想使用实例变量,我们应该为 bean 使用适当的范围。
或者完全避免使用实例变量。