我试图弄清楚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增强器在方法中生成的。
唯一想到的是ThreadLocal
DispatcherServlet
持有对MethodInterceptor
的引用,该方法具有ThreadLocal
字段,然后将实际对象(例如,通过getAttribute从会话中检索或仅通过HttpServletRequest
)注入该字段,然后拦截器使用实际的bean(存储在本地线程中),并使用反射调用原始bean上的方法。
任何建议或有趣的链接感谢<谢谢。
如果您使用的是请求范围的bean,那么Spring只需为它处理的每一个新的HTTP请求创建一个全新的实例。所以当然,在这种情况下,不会有任何线程安全问题。
但是,由于您没有共享任何代码,我没有完全理解您所说的在控制器中有
HttpServletRequest
字段。您可以将一个HttpServletRequest
作为控制器方法的方法参数,这很好,但您永远不会将它作为控制器本身的字段。
对于会话范围的bean,它类似于请求范围的bean——为每个新的
HttpSession
创建一个新实例。
只有应用程序上下文相互注入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