【问题标题】:Order of loading contextConfigLocation in web.xml of Spring Servlet projectSpring Servlet 项目的 web.xml 中加载 contextConfigLocation 的顺序
【发布时间】:2015-02-16 19:53:39
【问题描述】:

假设我有一个 Spring Java 项目,并且我正在尝试将其配置为 Web 服务器 servlet。这是 web.xml 文件的精简版:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        /WEB-INF/spring/generalApplicationContext.xml
    </param-value>
</context-param>

<servlet>
    <servlet-name>my-servlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/specificApplicationContext.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>my-servlet</servlet-name>
    <url-pattern>/foo/*</url-pattern>
</servlet-mapping>

这里要注意的关键是我已经指定了两个要加载的 XML 文件。一个对我的整个应用程序是通用的,而另一个特定于“my-servlet”servlet。对于只有一个 servlet 映射的设置,这是没有意义的。但是,我的项目有多个 servlet 映射,每个映射都有特定的 Spring 设置。

我的问题: Spring 将首先加载哪个 contextConfigLocation?它是generalApplicationContext.xml 还是specificApplicationContext.xml?更重要的是,加载顺序是否重要?从我的调试工作来看,它似乎确实如此,因为当我将一些独立的 Spring 配置从一个文件移动到另一个文件时,我得到了不同的错误。

注意: 对多个 servlet 映射使用多个 spring 配置是否是一种好做法是值得商榷的。使用 XML 配置而不是新的 Java 配置也是如此。但这不是我想在这里问的。让我们试着专注于我的主要问题。

【问题讨论】:

  • 你在 DispatcherServlet 中声明的那个我不确定,&lt;load-on-startup&gt;1&lt;/load-on-startup&gt; 因为这个我认为。
  • 要完全加载 contextConfigLocation / generalApplicationContext.xml,您必须有一个 ContextLoaderListener - 请参阅 shazin 的答案。为了更深入地了解什么是有用的,请参阅此问题和答案:stackoverflow.com/questions/9016122/…
  • NB是什么意思?

标签: java spring spring-mvc servlets web.xml


【解决方案1】:

有什么更好的方法让 Spring 调试日志告诉你自己的顺序。 如果您想进入代码,您还可以查看org.springframework.web.servlet.FrameworkServletDispatcherServlet 扩展了此类)只需启用记录器"org.springframework.web.servlet" 以在您首选的日志记录框架中调试级别

以下是日志的典型外观 - 显然,根上下文首先加载并设置为上下文层次结构的父级 - 接下来加载 servlet 上下文。

INFO : org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from ServletContext resource [/WEB-INF/spring/generalApplicatonContext.xml]
INFO : org.springframework.web.context.ContextLoader - Root WebApplicationContext: initialization completed in 256 ms
DEBUG: org.springframework.web.servlet.DispatcherServlet - Initializing servlet 'my-servlet'
INFO :Initializing Spring FrameworkServlet 'appServlet'
INFO : org.springframework.web.servlet.DispatcherServlet - FrameworkServlet 'my-servlet': initialization started
DEBUG: org.springframework.web.servlet.DispatcherServlet - Servlet with name 'appServlet' will try to create custom WebApplicationContext context of class 'org.springframework.web.context.support.XmlWebApplicationContext', using parent context [Root WebApplicationContext: startup date [Fri May 15 17:08:24 IST 2015]; root of context hierarchy
DEBUG: Loading XML bean definitions from ServletContext resource [/WEB-INF/spring/specificApplicationContext.xml

【讨论】:

  • 这是否意味着“specificApplicationContext.xml”应该只有与@controllers和view Resolvers相关的配置?而“generalApplicatonContext.xml”将保存应用程序级别的配置,如事务管理器和服务bean以及石英调度程序,我的意思是所有控制器都可以使用的配置,为什么我们不能在“generalApplicatonContext.xml”中进行所有配置?
【解决方案2】:

如果您的 web.xml 中有 ContextLoaderListener,spring 将首先加载 generalApplicationContext.xml。这将创建 bean 并为它们提供所有 Servlet 和过滤器。这个 xml 应该有你的应用程序中使用的公共类,bean。

稍后 spring 容器将加载 specificApplicationContext.xml,因为您在 servlet 配置中已加载启动。如果您未指定启动时的负载,则此 specificApplicationContext.xml 将在 first request 以特定 url-pattern 到达您的应用程序时加载。

作为您的问题,当您将 springconfig 从一个配置移动到另一个配置时,这将改变容器的应用程序资源可用性。如果您在 generalApplicationContext.xml 中指定了 Controller bean 而您没有在 specificApplicationContext.xml 中指定它们,那么您的 DispatcherServlet 将找不到映射,因此您将看到 404 错误。

如果您想按需创建一些 bean 对象,您可以再创建一个 servlet-config 来加载特定的配置文件 2.xml,并映射到 url-pattern。

【讨论】:

    【解决方案3】:

    以下部分加载上下文文件并创建 ApplicationContext。例如,此上下文可能包含组件,例如中间层事务服务、数据访问对象或您可能希望在应用程序中使用(和重用)的其他对象。每个应用程序将有一个应用程序上下文。

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring/generalApplicationContext.xml
        </param-value>
    </context-param>
    

    另一个上下文是WebApplicationContext,它是应用程序上下文的子上下文。在 Spring Web 应用程序中定义的每个 DispatcherServlet 都会有一个关联的 WebApplicationContextWebApplicationContext 的初始化如下:

    <servlet>
        <servlet-name>my-servlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring/specificApplicationContext.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    

    更多详情请参考thisthis

    【讨论】:

    • 您所写的在大多数情况下都是正确的,但在这一次中则不然,因为ecbrodie 在他的xml 中没有ContextLoaderListener 或者他在问题中无意中省略了它。
    • 我没有发布完整的 web.xml。我一有机会就会这样做。
    • @ankur 你的父 ApplicationContext 也是 WebApplicationContext 的实例。
    【解决方案4】:

    generalApplicationContext.xml 将首先加载,因为它是 ApplicationContextContextLoaderListener 一起加载

    <listener>
         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring/generalApplicationContext.xml
        </param-value>
    </context-param>
    

    specificApplicationContext.xml实际上是上面加载的generalApplicationContext.xml的子上下文,它将是WebApplicationContext

    <servlet>
        <servlet-name>my-servlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring/specificApplicationContext.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>my-servlet</servlet-name>
        <url-pattern>/foo/*</url-pattern>
    </servlet-mapping>
    

    是的,加载顺序很重要。因为在加载父上下文时,必须满足所有必需的依赖项。

    【讨论】:

    • 你是如何将ApplicationContextWebApplicationContext 命名为的?
    • 我知道WebApplicationContextApplicationContext 的子类型。但是我想知道ContextLoaderListener 究竟是什么,ApplicationContextWebApplicationContext。如果它创建ApplicationContext 实例,它肯定不等同于WebApplicationContext,因为前者不提供后者的功能。是不是就像,准确地说,ContextLoaderListener 创建了WebApplicationContext,它也提供了ApplicationContext 的功能,是它的子类型? [继续...]
    • [...continued] This answer 表示它同时创建了 ApplicationContextWebApplicationContext。 ? 现在尝试从source 了解它到底做了什么。但它没有任何意义......
    • [...continued] 好的,看看source(也是它的supertype),它似乎创建了WebApplicationContext。解释这个话题的文章说ApplicationContext 是为了方便(或者隐含地假设读者会知道我们总是说WebApplicationContext?)。
    • [...contined 4] 是不是就像ContextLoaderListener 创建的WebApplicaionContextusually online articles refer 作为“root” WebApplicationContext 一样,只是为了将其与 DispatcherServlet 创建的 WebApplicationContexts 区分开来?
    猜你喜欢
    • 2014-02-26
    • 1970-01-01
    • 2016-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-20
    • 2014-10-03
    • 1970-01-01
    相关资源
    最近更新 更多