【问题标题】:Spring controllers - thread safety and storing resourcesSpring 控制器 - 线程安全和存储资源
【发布时间】:2012-08-20 17:35:55
【问题描述】:

我有一个关于 spring mvc 和线程安全的问题。

我们正在开发将存储在 tomcat 上的 Web 应用程序。如果我理解正确,Tomcat 会为每个请求创建线程并且它有一些线程池。现在,调度程序 servlet 在请求之间共享,并且可能是线程安全的。

但是当我这样创建控制器时:

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

他拥有Singleton 范围,因此来自每个用户的每个请求都使用相同的控制器。

我想知道这个问题通常是如何解决的:

1:控制器是否使用Session 范围创建? (但我认为,如果一个用户快速执行一些可能导致控制器出现竞争条件的事情,也可能会出现问题。

2:控制器的作用域为request

3:创建在类级别不共享任何变量的无状态控制器,或者让它们处于只读模式

或者也许有一些更好的“最佳实践”可以解决这类问题。

我在问这个问题,因为现在我们将它们作为Singleton 作用域,并且存在一个问题,在大多数方法中,我们正在查询数据库中的用户,并且我们无法将这些信息存储在类级别由于范围而变量。我们可以尝试使用一些线程安全的集合,但稍后可能会有其他资源需要同步访问。

【问题讨论】:

  • 您不能在实例变量中存储什么信息?
  • User 类的对象,如果有多个用户请求,如果控制器的范围是单例,我需要存储此信息是某种同步集合。这就是为什么我想知道是否应该将它们设为Session 作用域。
  • 为什么需要将它存储在控制器中?你不能从会话中得到它吗?
  • 你的意思是,用会话对象存储这些信息?
  • 会话级用户信息还能去哪里?

标签: java tomcat spring-mvc thread-safety


【解决方案1】:

lot of parameters can be added 到控制器方法,如请求、会话、主体等,以避免您的问题。

通常有 3 层架构:

  • @Controller(他们委托给服务)
  • @Service(他们使用 DAO 或存储库完成工作)
  • @Repository(或 DAO,他们进行数据库访问)

因此,根据您在数据库中查询的内容,我建议您使用一个由 Spring 注入缓存的服务,如果访问数据库的成本很高,并且每次您需要数据库中的内容时调用该服务(即没有存储任何内容)在控制器类级别)

一个简短的例子,假设我们支持 spring-security 并且一切都需要一个完全登录的用户。我们有一个userData 表,其中的键是用户登录,我们有一个 url /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.
    }
}

这里我使用principal和spring确保这是当前用户之一。

参考:

请注意此答案是否完全符合您的担忧

【讨论】:

  • 在某种程度上,@Cacheable 很有趣,我会研究一下,但是将控制器设为单例是常见的做法吗?
  • 链接“可以添加很多参数”已失效。
  • 尽管您的答案已被接受为正确的,但我看不出它如何回答 OPs 的问题。那些众多的参数并不能说明控制器是否必须是线程安全的。
  • 如果由 spring 管理(即@Controller),它是单例的(如果您依赖 spring 获取控制器实例并且此控制器不在原型范围内,请参阅 spring 文档)
猜你喜欢
  • 2012-12-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-14
  • 2017-06-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多