【问题标题】:Inject HttpServletRequest into Controller将 HttpServletRequest 注入控制器
【发布时间】:2014-03-26 22:06:53
【问题描述】:

据我所知,默认情况下是 Spring MVC 单例中的控制器。 HttpServletRequest 将 off 传递给控制器​​处理程序方法。没关系,虽然 HttpServletRequest 是请求范围的,但我经常看到 HttpServletRequest@Autowired 放入控制器字段,如下所示:

@Controller("CMSProductComponentController")
@RequestMapping(CMSProductComponentController.CONTROLLER_PATH)
public class CMSProductComponentController {
    @Autowired
    private HttpServletRequest request;
}

这可能是个问题吗?还有更一般的问题:如果将一个请求范围的组件注入到单例中会发生什么?

【问题讨论】:

标签: java spring spring-mvc


【解决方案1】:

不,对于HttpServletRequest,这不会是一个问题,对于其他请求范围的bean也不应该。基本上,Spring 将生成一个代理HttpServletRequest,它包装了某种知道如何检索实际实例的ObjectFactoryRequestObjectFactory for HttpServletRequest)(YMMV)。当您使用此代理的任何方法时,它们将委托给该实例。

更重要的是,这是延迟完成的,所以它不会在初始化时失败。但是,如果您在没有可用请求的情况下尝试使用 bean(或者如果您尚未注册 RequestScope),它将失败。


以下是针对cmets的回应和一般性的澄清。

关于@Scopeproxy-mode 属性或等效的XML,默认 ScopedProxyMode.NO。但是,正如 javadoc 所述

这种代理模式在与非单例一起使用时通常没有用处 范围内的实例,应该有利于使用 INTERFACES 或 如果将其用作依赖项,则改为使用 TARGET_CLASS 代理模式。

对于请求范围的 bean,此 proxy-mode将不起作用。您需要使用 INTERFACESTARGET_CLASS,具体取决于您想要的配置。

scope 设置为request(使用常量WebApplicationContext.SCOPE_REQUEST),Spring 将使用RequestScope 其中

依赖于线程绑定的RequestAttributes 实例,可以是 通过RequestContextListenerRequestContextFilter 或导出 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()

documentation 状态

Spring IoC 容器不仅管理您的实例化 对象(bean),还有协作者(或 依赖项)。 如果你想注入(例如)一个 HTTP 请求 作用域 bean 到另一个 bean,你必须在适当的位置注入一个 AOP 代理 范围 bean。 也就是说,您需要注入一个代理对象 公开与作用域对象相同的公共接口,但可以 还从相关范围检索真实的目标对象(对于 例如,一个 HTTP 请求)并将方法调用委托给真实的 对象。

如果正确实施,所有急切加载的请求范围 bean 都将成为代理。类似地,未急切加载的请求范围 bean 要么是代理本身,要么是通过代理加载。如果没有HttpSerlvetRequest 绑定到当前线程,这将失败。基本上,对于请求范围的 bean,在 bean 依赖链的某个地方需要一个代理。

【讨论】:

  • 这个 Spring HttpServletRequest 代理行为是否记录在任何地方?似乎是一个相当有用的功能,但我没有找到任何权威文档的运气。
  • @M.Justin 我已经离开 Spring MVC 一段时间了。乍一看,它似乎分布在整个 Javadoc 中。从 @RequestScope 注释和处理它的 RequestScope 类开始。这将引导您访问RequestAttributes,它提供对当前HttpServletRequest 的访问权限。
【解决方案2】:

如果将一个请求范围的组件注入到单例中会发生什么?

尝试一下,您将在应用程序上下文初始化期间获得BeanCreationException¹。错误消息清楚地解释了为什么 HttpServletRequest 不会发生这种情况:

作用域“请求”对当前线程无效;如果您打算从单例中引用它,请考虑为该 bean 定义一个作用域代理

显然HttpServletRequest 是一个作用域代理。如果你想在单例中使用较小范围的 bean,它们必须是代理。该文档在 Scoped beans as dependencies 中详细说明了较小范围的依赖项。

[1]:除非您没有将default behaviour 更改为proxyMode,即NO,或者尝试使用@Lazy 注入它。后者可能会导致有效的应用程序上下文,但可能会导致请求范围的 bean 像单例一样(例如,如果请求范围的 bean 被注入到单例中)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-18
    • 2016-11-16
    • 2016-01-26
    • 1970-01-01
    • 1970-01-01
    • 2016-04-09
    相关资源
    最近更新 更多