ContextLoaderListener 与否?

Posted

技术标签:

【中文标题】ContextLoaderListener 与否?【英文标题】:ContextLoaderListener or not? 【发布时间】:2012-02-19 10:17:10 【问题描述】:

一个标准的 Spring Web 应用程序(由 Roo 或“Spring MVC 项目”模板创建)创建一个带有 ContextLoaderListenerDispatcherServlet 的 web.xml。 为什么他们不仅使用DispatcherServlet 并使其加载完整的配置?

我知道 ContextLoaderListener 应该用于加载与 Web 无关的内容,而 DispatcherServlet 用于加载与 Web 相关的内容(控制器,...)。这会导致两个上下文:父上下文和子上下文。

背景:

多年来,我一直以这种标准方式进行操作。

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:META-INF/spring/applicationContext*.xml</param-value>
</context-param>

<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- Handles Spring requests -->
<servlet>
    <servlet-name>roo</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>WEB-INF/spring/webmvc-config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

这通常会导致两个上下文以及它们之间的依赖关系出现问题。在过去,我总是能够找到解决方案,并且我有强烈的感觉,这使得软件结构/架构总是更好。但现在我面对的是problem with the events of the both contexts。

-- 然而,这让我重新思考这两种上下文模式,我在问自己:我为什么要让自己陷入这个麻烦,为什么不使用一个DispatcherServlet 加载所有弹簧配置文件并完全删除ContextLoaderListener。 (我仍然会有不同的配置文件,但只有一个上下文。)

有什么理由不删除ContextLoaderListener

【问题讨论】:

"这通常会导致两个上下文以及它们之间的依赖关系出现问题。"这是一个很好的例子,我认为依赖注入框架只会让我们的生活比自己动手的依赖注入更难。 @Andy - 虽然我对这个观点有些同情,但我不禁注意到您需要两种上下文的用例(在安全过滤器和 servlet 之间共享对象,自动管理事务所以它们在您重定向到的视图完成渲染后关闭)如果没有框架的帮助很难实现。这主要是因为 servlet API 显然根本没有设计为使用依赖注入,如果你尝试自己做,它会积极地对你不利。 @PeriataBreatta 我明白了!那么,你认为如果它的设计不同,Spring MVC 会有更好的替代品吗?尽管人们无论如何都可以设计出 Servlet API 的完整替代方案...... @PeriataBreatta 有趣的是,在 JS 世界中,我已经使用 Express 路由 HTTP 请求大约一年了,我很少看到任何提及“依赖注入”并且没有类似的东西完全是 Spring 框架。 【参考方案1】:

在您的情况下,不,没有理由保留 ContextLoaderListenerapplicationContext.xml。如果您的应用只使用 servlet 的上下文就可以正常工作,那么坚持下去,它会更简单。

是的,普遍鼓励的模式是将非网络内容保留在 webapp 级别的上下文中,但这只不过是一个弱约定。

使用 webapp 级上下文的唯一令人信服的理由是:

如果您有多个DispatcherServlet 需要共享服务 如果您有需要访问 Spring-wired 服务的旧版/非 Spring servlet 如果您有 servlet 过滤器可以连接到 webapp 级上下文(例如 Spring Security 的 DelegatingFilterProxyOpenEntityManagerInViewFilter 等)

这些都不适用于您,因此额外的复杂性是不必要的。

将后台任务添加到 servlet 的上下文时要小心,例如计划任务、JMS 连接等。如果您忘记将&lt;load-on-startup&gt; 添加到您的web.xml,那么这些任务将在第一次访问之前不会启动servlet。

【讨论】:

监听器呢,它们需要由 Context Loader 监听器创建的 Context(IllegalStateException,未找到 WebApplicationContext,由 MultipartFilter、CharacterEncodingFilter、HiddenHttpMethodFilter、Spring Security DelegatingFilterProxy 和 OpenEntityManagerInViewFilter 触发)。反过来做是不是一个好主意(通过 ContextLoaderListener 加载所有内容,并在没有配置的情况下保留 DispatcherServlet)? @Ralph:很好,我已将该用例添加到列表中。至于在没有配置的情况下离开DispatcherServlet - 如果你这样做了,你将没有网络界面。所有 MVC 的东西都必须放在那里。 @skaffman 为什么在使用带有 DelegatingFilterProxy 的 spring-security 时应该使用两个上下文?在我的例子中,spring-security bean 和默认的 spring 上下文共享一些 bean。所以他们也应该共享相同的上下文。还是应该将 spring 安全 bean 排除在默认的 spring 上下文之外?【参考方案2】:

您也可以反过来配置应用程序上下文。例如。为了使 OpenEntityManagerInViewFilter 工作。设置 ContextLoaderListener,然后配置您的 DispatcherServlet:

<servlet>
    <servlet-name>spring-mvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value></param-value>
    </init-param>
</servlet>

只需确保 contextConfigLocation 参数值为空即可。

【讨论】:

但是这种配置有什么好处呢? “反过来”是什么意思? “skaffman”的解决方案只配置了一个 Web 应用程序上下文 (servlet)。但是,使用这种方法,您会遇到解决方案本身中详述的问题:“使用 webapp 级上下文的唯一令人信服的理由是:” ...“如果您有挂钩到 webbapp 级上下文的 servlet 过滤器(例如Spring Security 的 DelegatingFilterProxy、OpenEntityManagerInViewFilter 等)”如果您只想使用 1 个应用程序上下文 XML 文件,我认为我的解决方案(通过 ContextLoaderListener 指定 XML)会更好。 你能在 Context Listener 创建的 Context 中使用 MVC Web Controller 吗? 是的。您只需在 Context Listener 指定的 context.xml 文件中设置您的控制器。它的工作方式是 DispatcherServlet 将简单地加入“父应用程序上下文”(上下文侦听器)。当您将“contextConfigLocation”值留空时,Context Listener 指定的 context.xml 文件将被独占使用。 我认为您在上下文中错过了 。 @GunnarHillert 解决方案对我有用。【参考方案3】:

我想分享一下我在 Spring-MVC 应用程序上所做的事情:

    we-mvc-config.xml 上,我只添加了带有@Controller 注释的类:

    <context:component-scan base-package="com.shunra.vcat">
        <context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
    </context:component-scan>
    

    applicationContext.xml 文件上,我添加了所有其余部分:

    <context:component-scan base-package="com.shunra.vcat">
        <context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
    </context:component-scan>
    

【讨论】:

是的,这是一个有用的模式。另一个有用的模式是将您的数据库处理 bean 放在应用程序上下文中(OpenSessionInViewFilter 或类似的可能需要这些)以及过滤器或侦听器特别需要的任何内容(例如使用 spring 安全性所需的定义)。

以上是关于ContextLoaderListener 与否?的主要内容,如果未能解决你的问题,请参考以下文章

3ContextLoaderListener做了哪些事

ContextLoaderListener作用详解

ContextLoaderListener

ContextLoader 和 ContextLoaderListener 的区别

ContextLoaderListener的作用

HttpPutFormContentFilter 和 ContextLoaderListener 讲解