如果您使用Spring MVC,您的组件类(@Controller
,@Service
,@Repository
)必须是线程安全的吗?
也就是说,如果我的@RequestMapping
中有@Controller
方法,是否可以通过多个线程同时为同一控制器对象调用该方法?
(这有sort-of been asked before,但没有这样回答。)
答案 0 :(得分:44)
鉴于
@Controller
public class MyController {
@RequestMapping(value = "/index")
public String respond() {
return "index";
}
}
Spring将创建MyController
的实例。这是因为Spring解析了您的配置<mvc:annotation-driven>
,看到了@Controller
(类似于@Component
)并实例化了带注释的类。由于它也会看到@RequestMapping
,因此会为其生成HandlerMapping
,请参阅docs here。
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类必须是线程安全的。您可以通过为类实例字段播放不同的范围或仅使用局部变量来实现此目的。如果做不到这一点,您需要在代码中的关键部分周围添加适当的同步。
答案 1 :(得分:1)
默认情况下,控制器是单例,因此必须是线程安全的。但是,您可以将控制器配置为请求或会话范围,即:
@Controller
@Scope("session")
public class MyController {
...
}
具有会话范围的控制器可以帮助管理会话状态。可以在Using Sessions in Spring-MVC (including "scoped-proxies")和How to get Session Object In Spring MVC中找到有关不同模式的详细说明。一些呈现的模式需要请求范围。
如果您的数据无法为每个请求计算多个数据,那么请求范围也很有用。
答案 2 :(得分:-1)
是的,当然。
最好是那些是无状态的,这使得它们在默认情况下是线程安全的。如果没有共享的,可变的状态则没有问题。
答案 3 :(得分:-2)
基本上答案应该是肯定而不是。除了非常严重的原因。不是因为Spring同步为你工作 - 它没有这样做(默认情况下,Controller是一个Singleton bean)。粗略的线程安全必须保持在方法调用,但通常Servlet的机制消除了同步某事的必要性,因为在线程内执行的请求。因此,在调用任何@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成为瓶颈!关于控制器完全相同。一个doGet / Post / Servlet的其他方法基本上都是一个功能模型,所有数据都在HttpServletRequest对象中。应该在Controller对象使用的数据类中使用相同的方式,而使用堆栈而不是字段。您可以通过支付瓶颈价格来同步对它们的访问权限。粗糙你可以使用原子,是否需要? 如果你需要在会话期间保持任何状态,你可以在@Controller之后使用@Scope(&#34; session&#34;)或者(对于单身控制器)使用带签名(...,HttpSession)的处理程序方法赞成和反对。但请注意,您创建了额外的CPU和GC费用。 无论如何,当您想要使用字段并退出无状态概念时,您负责控制器线程安全。通常,缓存(例如redis)对于客户端状态保持是优选的。至少可以在发生故障时恢复状态。超出会话范围的有状态控制器基本上没有理由。