Spring Servlet 项目的 web.xml 中加载 contextConfigLocation 的顺序

Posted

技术标签:

【中文标题】Spring Servlet 项目的 web.xml 中加载 contextConfigLocation 的顺序【英文标题】:Order of loading contextConfigLocation in web.xml of Spring Servlet project 【发布时间】: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 的答案。为了更深入地了解什么是有用的,请参阅此问题和答案:***.com/questions/9016122/… NB是什么意思? 【参考方案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>

更多详情请参考this和this

【讨论】:

您所写的在大多数情况下都是正确的,但在这一次中则不然,因为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 创建的WebApplicaionContext 是usually online articles refer 作为“root” WebApplicationContext 一样,只是为了将其与 DispatcherServlet 创建的 WebApplicationContexts 区分开来?

以上是关于Spring Servlet 项目的 web.xml 中加载 contextConfigLocation 的顺序的主要内容,如果未能解决你的问题,请参考以下文章