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 使用适当的范围。

或者完全避免使用实例变量。