名为“applicationContext.xml”的文件没有什么特别之处,只是 Spring 倾向于将其命名为默认配置文件。使用一个名为该文件的文件或多个名为“dog.xml”、“cat.xml”和“alien.xml”的文件的工作方式完全相同。您遇到的麻烦来自同时使用多个 ApplicationContexts,而不是来自拥有多个 XML 文件。我最近回答了一些因不理解这些概念而遇到问题的人提出的问题。查看这些答案,看看您还有什么问题:
Declaring Spring Bean in Parent Context vs Child Context
Spring-MVC: What are a "context" and "namespace"?
编辑:回答您的新问题:
我的 servlet.xml 中有一个 <context:component-scan base-package="com.myapp"/> 标记。
我猜这个“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 启动时,它将创建一个由文件 foo-servlet.xml 配置的新 ApplicationContext,该文件派生自 servlet-name。现在,由于您将context:component-scan 放在那里,它将递归地扫描给定的包并为所有带注释的类创建bean。你给它的包,com.myapp,看起来它是你整个应用程序的基础包,所以 Spring 将从你应用程序中的 所有 注释类创建 bean,包括数据访问类,在这是一个与 DispatcherServlet 关联的 ApplicationContext。通常,这个上下文应该只有视图层的东西和直接支持 DispatcherServlet 的 bean,所以这是一个错误的配置。
在我的 data.xml 文件中,我有数据源 bean,就是这样。没有其他 bean,其他所有内容都是自动装配和注释的。
大概,这个“data.xml”文件就是您在contextConfigLocation context-param 中列出的文件。假设您还将 ContextLoaderListener 添加到您的 web.xml,例如
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
然后该文件将用于创建第二个 ApplicationContext——根上下文。这就是这个听众所做的。请注意,它实际上是从 all contextConfigLocation 中列出的文件构建上下文,如果您还在该列表中包含了“servlet.xml”,那么您已经加载了两次该配置:here in根上下文以及与 DipatcherServlet 关联的上下文中。希望您现在看到 XML 配置文件和它们配置的 ApplicationContext 之间的明显区别。同一个 XML 文件可以很容易地用于配置两个不同的上下文。这样做是否正确是另一个问题。在这种特殊情况下,它不是。
我描述这两个上下文的顺序实际上是倒退的。我只是按照你对你所做的事情的描述。作为ServletContextListener 的ContextLoaderListener 将始终在任何servlet 启动之前执行。这意味着首先创建根上下文,然后创建另一个上下文。这是设计使然,当 DispatcherServlet 创建其上下文时,它可以将该上下文添加为根上下文的子上下文。我已经在其他帖子中描述了这种关系。这样做的最重要的影响是根上下文中的 bean 可用于并通过 DispatcherServlet 的上下文。这也适用于自动连接的关系。这很重要,因为 DispatcherServlet only 在其关联的上下文中查找它需要的 bean,例如控制器实例。但是,您的控制器显然必须与支持 bean 连接。因此,传统上,控制器存在于 DispatcherServlet 的上下文中,而支持的 bean 存在于根上下文中。
然后我尝试将 @Transacational 添加到我的服务 bean 中,但它不会持续存在。
为了使@Transactional 工作,您必须在注解bean 所在的ApplicationContext 的配置中包含<tx:annotation-driven/> 标签。诀窍是弄清楚“它住在哪里”部分。子项中的 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 在哪个上下文中运行。
这涵盖了所有内容吗?