将 HttpServletRequest 注入 Controller

2022-09-03 14:17:31

据我所知,默认情况下是Spring MVC单例中的控制器。 将 offen 传递给控制器处理程序方法。它还可以,虽然是请求范围的,但我看到经常进入控制器字段,如下所示:HttpServletRequestHttpServletRequestHttpServletRequest@Autowired

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

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


答案 1

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

更重要的是,这是懒惰地完成的,因此在初始化时不会失败。但是,如果您在没有可用的请求(或者尚未注册 )时尝试使用 Bean,它将失败。RequestScope


以下是对评论的回应,并一般澄清。

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

与非单例作用域实例一起使用时,此代理模式通常没有用处,如果要将其用作依赖项,则应倾向于使用 INTERFACES 或TARGET_CLASS代理模式。

对于请求范围的 Bean,此值将不起作用。您需要使用 OR,具体取决于所需的配置。proxy-modeINTERFACESTARGET_CLASS

设置为(使用常量WebApplicationContext.SCOPE_REQUEST),Spring将使用RequestScopescoperequest

依赖于线程绑定实例,该实例可以通过 或 导出。RequestAttributesRequestContextListenerRequestContextFilterDispatcherServlet

让我们举一个简单的例子

@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会将单例初始化为具有目标类类型的代理。在此示例中,即 。代理将包含在需要时生成或返回实际Bean所需的状态,即。在代理上调用方法时。例如,当RequestScopedBean

bean.method();

被调用。

此状态基本上是对请求范围的 Bean 定义的基础和名称的引用。它将使用这两个来生成一个新的 Bean,然后调用该实例。BeanFactorymethod()

文档说明

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

如果正确实现,所有预先加载的请求范围的Bean都将是代理。同样,未预先加载的请求范围 Bean 要么是代理本身,要么是通过代理加载。如果没有绑定到当前线程,这将失败。基本上,对于请求范围的Bean,Bean依赖关系链中的某个地方需要代理。HttpSerlvetRequest


答案 2

如果将要求范围的组件注入到单例中会发生什么情况?

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

当前线程的作用域“请求”不处于活动状态;如果您打算从单例引用此 Bean,请考虑为此 Bean 定义作用域代理;

所以显然是一个有作用域的代理。如果要在单例中使用较小范围的bean,它们必须是代理。本文档详细阐述了作用域 Bean 中较小作用域的依赖项作为依赖项HttpServletRequest

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


推荐