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 的顺序的主要内容,如果未能解决你的问题,请参考以下文章

Allocate exception for servlet XXX 基本异常

Druid数据源SQL数据库与Spring监控

Java Servlet接口web.xml配置HttpServlet父类

javaweb面试题

Spirng MVC启动流程

Servlet的生命周期及工作原理