[英]Inject HttpServletRequest into Controller
据我所知,默认情况下是Spring MVC单例中的控制器。 HttpServletRequest
将offen传递给控制器处理程序方法。 没关系,虽然HttpServletRequest
是请求范围的,但是我经常看到HttpServletRequest
被@Autowired
进入控制器字段,如下所示:
@Controller("CMSProductComponentController")
@RequestMapping(CMSProductComponentController.CONTROLLER_PATH)
public class CMSProductComponentController {
@Autowired
private HttpServletRequest request;
}
这可能是个问题吗? 还有一个更笼统的问题:如果将要求范围内的组件注入单例,会发生什么?
不,对于HttpServletRequest
来说这不是问题,对于其他请求范围的Bean也不应如此。 基本上,Spring将生成一个代理HttpServletRequest
,该代理包装了某种ObjectFactory
(用于HttpServletRequest
RequestObjectFactory
)(YMMV),该对象知道如何检索实际实例。 当您使用此代理的任何方法时,它们将委托给该实例。
而且,这是懒惰完成的,因此它不会在初始化时失败。 但是,如果在没有可用请求时尝试使用Bean(或者未注册RequestScope
),它将失败。
以下是对评论的回应,并进行了一般性的说明。
关于@Scope
或XML等效项的proxy-mode
属性,默认值为 ScopedProxyMode.NO
。 但是,正如javadoc所述
当与非单作用域实例一起使用时,此代理模式通常不是有用的,如果将其用作依赖项,则应优先使用INTERFACES或TARGET_CLASS代理模式。
对于请求范围的Bean,此proxy-mode
值将不起作用 。 您需要根据所需的配置使用INTERFACES
或TARGET_CLASS
。
将scope
设置为request
(使用常量WebApplicationContext.SCOPE_REQUEST
),Spring将使用RequestScope
依赖于线程绑定的
RequestAttributes
实例,该实例可以通过RequestContextListener
,RequestContextFilter
或DispatcherServlet
导出。
让我们举一个简单的例子
@Component
@Scope(proxyMode = ScopedProxyMode.INTERFACES, value = WebApplicationContext.SCOPE_REQUEST)
public class RequestScopedBean {
public void method() {}
}
...
@Autowired
private RequestScopedBean bean;
Spring将生成两个 bean定义:一个用于注入的bean,一个单例,一个用于在每个请求中生成的请求范围的bean。
通过这些bean定义,Spring会将单例初始化为具有目标类类型的代理。 在此示例中,这就是RequestScopedBean
。 代理将包含它需要产生或在需要时返回实际bean所需的状态,即。 在代理上调用方法时。 例如,当
bean.method();
叫做。
此状态基本上是对基础BeanFactory
的引用以及请求范围的Bean定义的名称。 它将使用这两个生成一个新bean,然后在该实例上调用method()
。
文档说明
Spring IoC容器不仅管理对象(bean)的实例化,而且还管理协作者(或依赖项)的连接。 如果要将(例如)HTTP请求范围的bean注入另一个bean,则必须注入AOP代理来代替范围的bean。 也就是说,您需要注入一个代理对象,该代理对象公开与范围对象相同的公共接口,但还可以从相关范围(例如,HTTP请求)中检索实际的目标对象,并将委托方法调用到实际对象上。
如果正确实现,所有热切加载的请求范围的Bean将成为代理。 同样,不急于加载的请求范围的Bean要么是代理本身,要么是通过代理加载的。 如果没有HttpSerlvetRequest
绑定到当前线程,则此操作将失败。 基本上,对于请求范围内的bean,在bean依赖关系链中的某个位置必须有一个代理。
如果将需求范围内的组件注入单例中会发生什么?
尝试一下,您将在应用程序上下文初始化期间得到BeanCreationException
。 错误消息清楚地说明了为什么HttpServletRequest
不会发生这种情况:
范围“请求”对于当前线程无效。 如果您打算从单例中引用它,请考虑为此bean定义作用域代理 。
因此,显然HttpServletRequest
是作用域代理。 如果要在单例中使用较小范围的bean,则它们必须是代理。 该文档详细介绍了Scoped bean中较小的范围依赖项作为依赖项 。
[1]:除非你没有更改默认行为的proxyMode
,这是NO
或尝试用注入它@Lazy
。 后者可能会导致进入有效的应用程序上下文,但可能导致请求范围内的bean像单例一样工作(例如,如果请求范围内的bean被注入到单例中)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.