当前位置: 首页 > 知识库问答 >
问题:

Spring MVC请求/会话作用域bean线程安全

巫马松
2023-03-14

我试图弄清楚spring是如何将线程安全的请求/会话范围的bean注入控制器组件(即通过方法访问这些bean的单线程和多线程)的
作为例子,考虑<代码> HttpServletRequest 字段,该控件标记为“代码> @ AutoWordEng/代码>注释(我知道将控制器与servlet API配对是不好的,但在学习目的上可以)。我了解到这样的bean是使用CGLib代理的,但仍然无法弄清楚代理如何处理线程安全,如何将bean作用域到当前线程。

这就是我到目前为止学到的:

>

  • 从请求到请求控制器字段指向同一实例(即使是HttpServletRequest
  • 来自会话作用域(代理)传输对象方法调用的线程堆栈跟踪

    java.lang.Thread.getStackTrace(Thread.java:1552)
    com.company.market.to.User.setUid(User.java:73)
    com.company.market.to.User$$FastClassBySpringCGLIB$$8eb69e9e.invoke(<generated>)
    org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720)
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:133)
    org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:121)
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655)
    com.company.market.to.User$$EnhancerBySpringCGLIB$$f4f46820.setUid(<generated>)
    com.company.market.controllers.UserController.signIn(UserController.java:97)
    

    正如您所看到的,一些代码是在运行时由CGLib增强器在方法中生成的。

    唯一想到的是ThreadLocalDispatcherServlet持有对MethodInterceptor的引用,该方法具有ThreadLocal字段,然后将实际对象(例如,通过getAttribute从会话中检索或仅通过HttpServletRequest)注入该字段,然后拦截器使用实际的bean(存储在本地线程中),并使用反射调用原始bean上的方法。

    任何建议或有趣的链接感谢<谢谢。

  • 共有2个答案

    漆雕兴平
    2023-03-14

    如果您使用的是请求范围的bean,那么Spring只需为它处理的每一个新的HTTP请求创建一个全新的实例。所以当然,在这种情况下,不会有任何线程安全问题。

    但是,由于您没有共享任何代码,我没有完全理解您所说的在控制器中有HttpServletRequest字段。您可以将一个HttpServletRequest作为控制器方法的方法参数,这很好,但您永远不会将它作为控制器本身的字段。

    对于会话范围的bean,它类似于请求范围的bean——为每个新的HttpSession创建一个新实例。

    蒙勇
    2023-03-14

    只有应用程序上下文相互注入bean,而没有其他人(不是DispatcherServlet或任何其他类)。所有的注射只在应用程序启动时发生一次。

    Spring并不关心组件的线程安全性。你应该自己做。这就是为什么官方文件建议:

    ...通常,对所有有状态bean使用prototype作用域,对无状态bean使用singleton作用域。

    让我们看看作用域bean是如何工作的。

    当Spring遇到作用域bean时,它找到相应的实现Scope接口来获取bean实例。例如,当你声明bean like时:

    @Bean
    @Scope(value="request")
    public RequestBean createBean(){
       return new RequsetBean();
    }
    

    并将其注入:

    @Autowired
    private RequestBean requestBean;
    

    spring将从RequstScope(此范围类默认注册)请求RequestBean的实例,并将其注入另一个bean。所以,只有Scope类知道如何获取作用域bean的实例,您不应该关心它。顺便说一下,您可以编写自己的Scope实现,注册它,并在bean声明中使用。

    现在,关于作用域代理。

    正如我所说,所有注入只在应用程序启动时发生。这意味着,每次操作的对象都与最初在启动时注入的对象完全相同。例如,当注入请求范围的bean时,这不是您期望的行为。当然,您希望在每个请求中都获得这个bean的新实例。为此,如文档中所述,将bean声明为代理:

    @Bean
    @Scope(value="request")
    @ScopeProxy
    public RequestBean createBean(){
       return new RequsetBean();
    }
    

    在这种情况下,Spring将把bean包装成具有相同公共接口的代理对象。现在,每次调用bean的方法时,代理对象都会从范围中获取真正的对象,然后将它们委托给方法调用。

    工作方式几乎相同。Spring注入了特殊的代理对象,而不是真正的HttpServletRequest。唯一的区别是这个代理对象不使用Scope。当您调用它的方法时,这个prox-对象会得到真正的HttpServletRequest对象,并将所有调用委托给它。顺便说一下,根据源代码和留档,Spring似乎确实将请求数据保存在ThreadLocal中。

    因此,每个请求线程都有自己的HttpServletRequest实例,并且控制器的方法不需要同步。

     类似资料:
    • 目前正在用Spring Boot 2.0.0.M4、Spring 5.0.0.RC4和Reactor 3.1.0.RC1进行反应性编程。 如果没有,这是计划好的吗? 谢谢你抽出时间。

    • MyCart.java 这个MyCart Bean是每个HTTP会话实例化的名为CDI的Bean。 抽象类 问题描述 仅用于理解会话范围的CDI。我有两个JSP文件,如下所示 SetCDIBeanValue.jsp其中,我将获得命名会话CDI Bean(MyCart)的实例将It String属性的值设置为String值FROM_FIRST_JSP 因为这个会话的作用域是有限的,所以我想应该只有一

    • 问题内容: 我有一个控制器,希望每个会话都唯一。根据spring文档,实现有两个细节: 1.初始Web配置 为了支持在请求,会话和全局会话级别(Web范围的Bean)的Bean范围界定,在定义Bean之前,需要一些较小的初始配置。 web.xml如文档所示,我已经添加了以下内容: 2.范围豆作为依赖项 如果要将(例如)HTTP请求范围的bean注入另一个bean,则必须注入AOP代理来代替范围的b

    • 我知道无状态会话bean指的是EJB bean,而请求范围bean指的是CDI bean(或JSF托管bean),所以我将从Java EE教程中给出它们的两个定义开始。 无状态会话bean定义: 无状态会话bean不维护与客户端的会话状态。当客户机调用无状态bean的方法时,该bean的实例变量可能包含特定于该客户机的状态,但仅限于调用期间。当方法完成时,客户端特定状态不应保留。 用户在单个HTT

    • 恐怕这个问题会有点模糊,但这是... 我们注意到我们的JEE7 web应用程序中存在一些非常奇怪的、偶然的行为。有时,用户页面会突然开始显示来自完全不同用户会话的数据!到目前为止,我还没有能够复制这种现象,也没有在日志中找到任何问题的迹象,但是似乎一个用户的页面开始显示存储在@SessionScoped CDIBean中的数据,该CDIBean应该属于另一个用户的会话。 这种行为对任何人都有影响吗

    • 问题内容: 我将JSF 2用于视图,将Spring用于业务逻辑。我正在尝试使用注解(@Scope(“ session”))将会话范围设置为我的一个Spring bean,但是却遇到了这个异常: 我知道RequestContextListener。在我的web.xml中。我还添加了RequestContextFilter: 似乎没有任何作用。我究竟做错了什么?谢谢! 问题答案: 尝试使用aop:sc