Java Spring MVC类必须是线程安全的吗

Java Spring MVC类必须是线程安全的吗,java,spring-mvc,thread-safety,Java,Spring Mvc,Thread Safety,如果您使用SpringMVC,您的组件类(@Controller、@Service、@Repository)必须是线程安全的吗 也就是说,如果我的@Controller中有一个@RequestMapping方法,那么多个线程是否可以同时为同一个Controller对象调用该方法 (这个问题已经解决了,但没有得到相应的回答)。当然可以 最好是无状态的,这使得它们在默认情况下是线程安全的。如果没有共享的、可变的状态,就没有问题。 @Controller public class MyControll

如果您使用SpringMVC,您的组件类(
@Controller
@Service
@Repository
)必须是线程安全的吗

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

(这个问题已经解决了,但没有得到相应的回答)。

当然可以


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

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

Spring将创建MyController的一个实例。这是因为Spring解析您的配置,
,查看
@Controller
(类似于
@Component
)并实例化带注释的类。因为它也可以看到
@RequestMapping
,所以它会为它生成一个
HandlerMapping
,请参见

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

如果您有如下实例字段

@Controller
public class MyController {
    private int count = 0;
    @RequestMapping(value = "/index")
    public String respond() {
        count++;
        return "index";
    }
}
count
将是一种危险,因为它可能会被许多线程修改,对它的更改可能会丢失


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


所以是的,SpringMVC类必须是线程安全的。您可以通过为类实例字段使用不同的作用域或只使用局部变量来实现这一点。否则,您需要在代码的关键部分周围添加适当的同步。

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

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

    ...
}
具有会话作用域的控制器有助于管理会话状态。在和中可以找到对不同模式的良好描述。提出的一些模式需要请求范围


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

基本上答案应该是“是”和“否”。除非有非常严重的原因。并不是因为Spring为您进行同步,而是它没有这样做(默认情况下,控制器是单例bean)。当然,线程的安全性必须随着方法调用而保持,但通常servlet机制消除了同步某些内容的必要性,因为请求是在线程内部执行的。因此,在任何@RequestMapping注释方法的调用过程中,整个调用堆栈都在一个线程中执行。在根目录中,它从Servlet的服务do(Get,Post..)方法传出,然后处理程序映射到active,这是Spring构建的(参见示例)。解析URL后,Spring从处理程序映射调用处理方法。别耍花招了。因此,您可以在DispatchServlet的doPost(…)实例方法中工作。Servlet线程安全吗?没有粗糙的。 你能做到线程安全吗?是的,使用锁、同步等等,让你的Servlet成为瓶颈!控制器也是如此。Servlet的doGet/Post/what-else方法基本上有一个功能模型,所有数据都在HttpServletRequest对象中。在控制器对象使用的数据类和堆栈(而不是字段)中应使用相同的方式。您可以同步访问它们,但要付出瓶颈的代价。当然,你可以使用原子学,到那时,它是如此需要吗? 如果您需要在会话期间保持任何状态,您可以在@Controller之后使用@Scope(“session”),或者(对于singleton Controller)一个带有签名的处理程序方法(…,HttpSession),两者都有优点和缺点。但是请注意,您创建了额外的CPU和GC费用。
无论如何,当您想要使用字段并退出无状态概念时,您需要负责控制器线程安全。通常,缓存(例如redis)更适合用于客户端状态保持。至少可以在发生故障时恢复状态。超出会话范围的有状态控制器基本上没有理由。

“当然”:我认为这对初学者来说并不明显。要不要扩展一点?@Raedwalk-Spring只创建一个
@组件
类的实例(默认情况下),因此它们的所有实例字段都将为每个请求共享。Spring默认生成bean单例,但也有其他作用域:原型、每个请求、每个会话。其他有经验的开发人员可能会做出不同的选择。我更喜欢无状态的单例,因为我认为它们可以更好地扩展。“如果没有共享的、可变的状态”,几乎所有有趣的web应用程序都有共享的、可变的数据。在大多数情况下,共享数据位于数据库中,因此确保线程安全的工作在Hibernate/JPA中完成。大多数有趣的web应用程序没有共享状态。它们在会话中确保会话特定信息的安全,只有特定用户才能看到它。我认为你不明白线程安全意味着什么。关于编辑。Spring将在相同的
@Controller
类实例上调用相同的方法,请求的格式与
@RequestMapping
所要求的格式相同。“Spring将创建MyController的实例”(假设您仅指一个实例),您能否提供有关此语句的任何官方文档?正如我在另一个答案中所评论的,这一部分似乎有些混乱。官方的,但不是一个具体的地点。让我更新一下a