将HttpServletRequest注入控制器


问题内容

据我所知,默认情况下是Spring
MVC单例中的控制器。HttpServletRequest将offen传递给控制器​​处理程序方法。而且它的确定,虽然HttpServletRequest是请求范围,但我经常看到HttpServletRequest获取@Autowired到控制器领域,就像这样:

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

这可能是个问题吗?还有一个更笼统的问题:如果将要求范围内的组件注入单例,会发生什么?


问题答案:

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

而且,这是懒惰完成的,因此它不会在初始化时失败。但是,如果在没有可用请求(或尚未注册RequestScope)的情况下尝试使用Bean,它将失败。


以下是对评论的回应,并进行了一般性的说明。

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

当与非单作用域实例一起使用时, 此代理模式 通常不是有用的,
如果将其用作依赖项,则应优先使用INTERFACES或TARGET_CLASS代理模式。

对于请求范围的Bean,此proxy-mode将不起作用 。您需要根据所需的配置使用INTERFACESOR
TARGET_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()该实例。

文档的状态

Spring IoC容器不仅管理对象(bean)的实例化,而且还管理协作者(或依赖项)的连接。
如果要将(例如)HTTP请求范围的bean注入另一个bean,则必须注入AOP代理来代替范围的bean。
也就是说,您需要注入一个代理对象,该代理对象公开与范围对象相同的公共接口,但还可以从相关范围(例如,HTTP请求)中检索实际的目标对象,并将委托方法调用到实际对象上。

如果正确实现,所有热切加载的请求范围的Bean将成为代理。同样,不急于加载的请求范围的Bean要么是代理本身,要么是通过代理加载的。如果HttpSerlvetRequest当前线程没有绑定,这将失败。基本上,对于请求范围内的bean,在bean依赖关系链中的某个位置必须有一个代理。