36

如果您使用 Spring,您的组件类 ( @Controller, @Service, @Repository) 必须是线程安全的吗?或者 Spring 是否以线程安全的方式使用它们,因此您不必担心线程安全?

也就是说,如果我的 中有一个@RequestMapping方法@Controller,是否可以由多个线程同时为同一个控制器对象调用该方法?

(这已经被问过,但没有这样回答)。

4

4 回答 4

55

给定

@Controller
public class MyController {
    @RequestMapping(value = "/index")
    public String respond() {
        return "index";
    }
}

Spring 将创建一个MyController. 这是因为 Spring 会解析您的配置,<mvc:annotation-driven>看到@Controller(类似于@Component)并实例化带注释的类。因为它也能看到@RequestMapping,所以它会为它生成一个HandlerMapping,请参阅此处的文档

接收到的任何 HTTP 请求DispatcherServlet都将通过之前注册的方式发送到此控制器实例,并通过该实例上的 java 反射HandlerMapping调用。respond()

如果您有实例字段,例如

@Controller
public class MyController {
    private int count = 0;
    @RequestMapping(value = "/index")
    public String respond() {
        count++;
        return "index";
    }
}

count将是一个危险,因为它可能被许多线程修改并且对它的更改可能会丢失。

您需要了解 Servlet 容器是如何工作的。容器实例化 Spring MVC 的一个实例DispatcherServlet。容器还管理一个线程池,用于响应连接,即。HTTP 请求。当这样的请求到达时,容器从池中选择一个线程,并在该线程内执行service()调度DispatcherServlet到 Spring 为您注册的正确@Controller实例(从您的配置)的方法。

所以是的,Spring MVC 类必须是线程安全的。您可以通过为类实例字段使用不同的范围或仅使用局部变量来做到这一点。如果做不到这一点,您将需要围绕代码中的关键部分添加适当的同步。

于 2013-05-28T15:13:44.080 回答
0

默认情况下,控制器是单例的,因此必须是线程安全的。但是,您可以将控制器配置为请求或会话范围,即:

@Controller
@Scope("session")
public class MyController {

    ...
}

具有会话范围的控制器有助于管理会话状态。可以在使用 Spring-MVC 中的会话(包括“作用域代理”)如何在 Spring MVC 中获取会话对象中找到对不同模式的良好描述。一些呈现的模式需要请求范围

如果您拥有无法为每个请求计算多个数据的数据,则请求范围也很有用。

于 2014-04-15T11:22:20.047 回答
-1

是的当然。

最好是无状态的,默认情况下它们是线程安全的。如果没有共享的、可变的状态,就没有问题。

于 2013-05-28T15:03:34.767 回答
-1

基本上答案应该是肯定的和否定的。除了非常严重的原因。不是因为 Spring 会为您同步工作——它不会这样做(默认情况下,Controller 是 Singleton bean)。粗略的线程安全必须在方法调用中保持,但通常 Servlets 机制消除了同步某些东西的必要性,因为请求在线程内执行。因此,在调用任何 @RequestMapping 注释方法期间,整个调用堆栈都在一个线程中执行。在根中,它从 Servlet 的 service-do(Get, Post..) 方法传出,然后执行处理程序映射,由 Spring 构建(参见http://www.studytrails.com/frameworks/spring/spring-mvc -handler-mappings/ http://technicalstack.com/dispatcher-servlethandlermapping-controller/例如)。在 URL 解析后,Spring 从处理程序的映射中调用处理方法。没有其他技巧。所以想一想,你在一个 doPost(...) 中工作,例如 DispatchServlet 的实例方法。Servlet 线程安全吗?没有粗的。你能做到线程安全吗?是的,使用锁,同步,还有什么让你的 Servlet 成为瓶颈!控制器也是如此。Servlet 的 doGet/Post/what else 方法基本上都有一个功能模型,所有数据都在 HttpServletRequest 对象中。在控制器对象使用的数据类和堆栈而不是字段中应该使用相同的方式。您可以同步对它们的访问,但要付出瓶颈的代价。粗略的你可以使用原子,它是如此需要吗?如果您需要在会话期间保持任何状态,可以使用 @Scope("session" )在@Controller 或(对于单例控制器)带有签名(...,HttpSession)的处理程序方法之后,它们都有优点和缺点。但请注意,您创建了额外的 CPU 和 GC 费用。无论如何,当您想使用字段并退出无状态概念时,您需要负责控制器线程安全。通常,缓存(例如 redis)对于客户端状态保持更可取。至少您可以在发生故障时恢复状态。超出会话范围的有状态控制器基本上没有理由。至少您可以在发生故障时恢复状态。超出会话范围的有状态控制器基本上没有理由。至少您可以在发生故障时恢复状态。超出会话范围的有状态控制器基本上没有理由。

于 2018-02-14T13:08:54.220 回答