一尘不染

Spring XML文件配置层次结构帮助/说明

spring

当我第一次开始学习Spring时,事情是在applicationContext.xml文件中配置的。然后,当我开始专门阅读有关Spring的最新版本的书籍时,他们都通过单独的XML文件(例如myapp-servlet-xml,myapp-security.xml,myapp-service.xml等)完成了配置。在web.xml文件中配置contextConfigLocation。因此,例如,我一直遵循的代码将其作为contextConfigLocation:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        /WEB-INF/myapp-servlet.xml
        /WEB-INF/myapp-data.xml
    </param-value>
</context-param>

无论如何,最近我遇到了一个配置问题(此问题是由StackOverflow的乐于助人的人帮助我解决的)。这些书中的示例都没有applicationContext.xml文件,后来当我尝试向应用程序添加自动扫描和注释时,这会引起问题。我尝试将所有内容移至applicationContext.xml并删除其他文件,从而解决了问题。没什么改变,我只是将所有内容都放在applicationContext.xml中。

因此,这以及其他人的评论使我有点理解,即使你不创建applicationContext.xml,它也仍在使用,它是某种配置层次结构的顶层。我希望其他人可以向我解释这一切是如何工作的,因为我在任何地方都没有对此进行任何解释。

因此,例如,如果我将某些context:component-scan标记放入位于applicationContext.xml下的配置文件中,则可能导致某些类无法被扫描。那种性质的东西。我不了解优先级以及必须去哪些地方才能确保它在整个应用程序中都可见。如果有人可以清楚地解释它,或指向我提供解释它的资源,我将非常感谢,谢谢。希望我要问的是有道理的。


阅读 810

收藏
2020-04-12

共1个答案

一尘不染

名为“ applicationContext.xml”的文件没有什么特殊之处,只是它是Spring期望作为其默认配置文件的名称。使用一个名为该文件的文件或多个名为“ dog.xml”,“ cat.xml”和“ alien.xml”的文件将完全相同。你遇到的麻烦来自同时使用多个ApplicationContext,而不是来自多个XML文件。我最近回答了一些人的问题,这些人因不了解这些概念而遇到问题。查看这些答案,看看你还有什么问题:

编辑:针对你的新问题:

<context:component-scan base-package="com.myapp"/>我的servlet.xml中有一个标签。

我猜这个“ servlet.xml”文件的名称为foo-servlet.xml,其中在web.xml中配置的DispatcherServlet的名称为“ foo”,例如

<servlet>
    <servlet-name>foo</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>

按照惯例,当此DispatcherServlet启动时,它将创建一个新的ApplicationContext,该文件foo-servlet.xml由从派生的文件配置servlet-name。现在,由于你context:component-scan在其中放置了一个,它将以递归方式扫描给定的包并为所有带注释的类创建bean。你提供给它的包com.myapp看起来像是整个应用程序的基础包,因此Spring将在与DispatcherServlet关联的这一ApplicationContext中,从应用程序中所有带注释的类(包括数据访问类)创建Bean 。通常,此上下文中应只包含直接支持DispatcherServlet的视图层内容和bean,因此这是一种错误的配置。

在我的data.xml文件中,有数据源bean就是这样。没有其他的bean,其他所有东西都自动装配和注释。

大概此“ data.xml”文件就是你在contextConfigLocationcontext-param中列出的文件。假设你还将ContextLoaderListener添加到了中web.xml,例如

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

然后该文件将用于创建第二个ApplicationContext-根上下文。那就是这个听众所做的。请注意,它实际上是根据中列出的所有文件构建上下文的contextConfigLocation,并且如果你还在该列表中包括“ servlet.xml”,那么你已经将该配置加载了两次:在根上下文以及关联的上下文中与DipatcherServlet。希望你现在看到XML配置文件和它们配置的ApplicationContext之间如何进行明显的区分。相同的XML文件可以轻松地用于配置两个不同的上下文。这样做是否正确是另一个问题。在这种情况下,事实并非如此。

我描述这两个上下文的顺序实际上是倒退的。我只是在听你对你所做的事情的描述。作为ServletContextListener的ContextLoaderListener 将始终在任何Servlet启动之前执行。这意味着首先创建根上下文,然后创建另一个上下文。这是设计使然,以便DispatcherServlet创建其上下文时,可以将该上下文添加为根上下文的子级。我已经在其他文章中描述了这种关系。这最重要的效果是,根上下文中的bean可用于DispatcherServlet的上下文,也可以通过DispatcherServlet的上下文使用。这也适用于自动装配的关系。这很重要,因为仅 DispatcherServlet在其关联上下文中查找所需的bean,例如控制器实例。但是,你的控制器显然必须与支持bean连线。因此,传统上,控制器位于DispatcherServlet的上下文中,而支持bean则位于根上下文中。

然后,我尝试将@Transacational添加到我的服务bean中,它不会持久存在。

为了使@Transactional工作,你必须<tx:annotation-driven/>在带注释的Bean所在的ApplicationContext的配置中包括该标记。诀窍是弄清楚“它住的地方”部分。子级中的Bean可以覆盖父级上下文中的Bean。因此-我只是在这里猜测-如果你如上所述将所有bean都加载到DispatcherServlet上下文中,但是将其<tx:annotation-driven/>放在根上下文中,则可能在根上下文中有一个可正确事务处理的bean,但不是之所以使用它,是因为该副本与父/子层次结构中的servlet“更近”,并且它所在的上下文没有得到<tx:annotation-driven/>配置。

当我将servlet的context:component-scan标记更改为指向com.myapp.web,然后将一个context:component-scan标记添加到data.xml文件时,一切正常。

它仍然在一定程度上取决于你在哪个ApplicationContexts中包括哪些配置文件,但是至少可以说,通过这样做,你从DispatcherServlet的上下文中删除了很多导致问题的bean。特别是,在根上下文中正确配置的@Transactional Bean将不再在子上下文中被Bean遮蔽,并且将被注入到控制器中,因此你的持久性工作将可以正常工作。

所以…最主要的是,你有两个相关的ApplicationContext。你必须保持对这一事实的了解,并控制在哪种上下文中使用哪种bean。

2020-04-12