如果你使用Spring,你的组件类(
@Controller
,@Service
,@Repository
)必须是线程安全的吗?或者 Spring 是否以线程安全的方式使用它们,因此您不必担心线程安全?
也就是说,如果我的
@RequestMapping
中有一个 @Controller
方法,是否可以由多个线程同时为同一个控制器对象调用该方法?
(这已经 之前被问过,但没有这样回答)。
给出
@Controller
public class MyController {
@RequestMapping(value = "/index")
public String respond() {
return "index";
}
}
Spring 将创建一个
MyController
的实例。这是因为 Spring 解析您的配置,<mvc:annotation-driven>
,看到@Controller
(类似于@Component
)并实例化带注释的类。因为它也能看到 @RequestMapping
,所以它会为它生成一个 HandlerMapping
,请参阅此处的 docs。
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 请求。当这样的请求到达时,容器从池中选择一个线程,并在该线程中执行 service()
上的 DispatcherServlet
方法,该方法分派到 Spring 为您注册的正确的 @Controller
实例(根据您的配置) .
所以是的,Spring MVC 类必须是线程安全的。您可以通过为您的类实例字段使用不同的范围或只使用局部变量来做到这一点。如果做不到这一点,您将需要在代码的关键部分周围添加适当的同步。
是的,当然。
最好是无状态的,这使得它们在默认情况下是线程安全的。如果没有共享的、可变的状态就没有问题。
默认情况下,控制器是单例的,因此必须是线程安全的。但是,您可以将控制器配置为请求或会话范围,即:
@Controller
@Scope("session")
public class MyController {
...
}
具有 session scope 的控制器有助于管理会话状态。可以在在 Spring-MVC 中使用会话(包括“作用域代理”)和如何在 Spring MVC 中获取会话对象中找到对不同模式的很好描述。一些呈现的模式需要 request scope.
如果您的数据无法承受每个请求计算一个以上的数据,则请求范围也很有用。
一个 Spring 组件,以及另一个类,如果它可以被多个线程并发使用,那么它必须是线程安全的。默认情况下,Spring 组件是单例的,可以从不同的线程调用。从这点来看,它的STATE应该是线程安全的。 另一方面,单例 Spring bean 通常在线程池的线程调用堆栈中为请求提供服务。通常 Spring 组件具有字段,这些字段的值是注入的接口或属性。唯一需要注意安全的是将状态对象保存在 bean 中的情况。通常,由于服务器是负载平衡的并且状态保存在缓存或数据库中,因此不使用它。 因此,Singleton bean 应该用作一组函数,它将数据作为相关方法的参数进行传输,其字段不应该保留业务数据,而只是注入的 bean,为调用进程提供服务。 Spring 的控制器就是一个例子。它的方法由Tomcat的线程调用,控制器方法映射的方式,所有调用堆栈都在一个线程中完成。控制器的方法调用 DAO 方法并在同一个线程中返回结果。 Session 通常在会话之间持久化,这由 Spring 处理。 所以,除了极少数情况,没有必要考虑线程安全。