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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7774295/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 04:41:47  来源:igfitidea点击:

Spring XML file configuration hierarchy help/explanation

spring

提问by cardician

When I first started learning about Spring, things were configured in the applicationContext.xml file. Then as I started to read books specifically on more recent versions of spring, they've all done the configuration in separate XML files such as myapp-servlet-xml, myapp-security.xml, myapp-service.xml, etc., by configuring a contextConfigLocation in the web.xml file. So, for instance, the code I've been following along with had this as it's contextConfigLocation:

当我第一次开始学习 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>

Anyway, recently I ran into a configuration issue (which the helpful people here at StackOverflow helped me figure out) that was due to this separation. There was no applicationContext.xml file for the examples from these books and later on when I tried adding automatic scanning and annotations to the app this caused issues. I tried moving everything into applicationContext.xml and doing away with the other files and that solved the problem. Nothing else changed, I just put everything in applicationContext.xml.

无论如何,最近我遇到了一个配置问题(StackOverflow 上的有用人员帮我解决了这个问题),这是由于这种分离造成的。这些书中的示例没有 applicationContext.xml 文件,后来当我尝试向应用程序添加自动扫描和注释时,这导致了问题。我尝试将所有内容移动到 applicationContext.xml 中并取消其他文件并解决了问题。没有其他改变,我只是把所有东西都放在 applicationContext.xml 中。

So, this, along with comments from others, has lead to me sort of understand that even if you don't create an applicationContext.xml, it's still being used and it is the top level of some sort of configuration hierarchy. I'm hoping someone else can explain to me how this all works because I've not come across any explanation on it anywhere.

因此,这与其他人的评论一起使我明白,即使您不创建 applicationContext.xml,它仍会被使用,并且它是某种配置层次结构的顶层。我希望其他人可以向我解释这一切是如何运作的,因为我在任何地方都没有遇到过任何解释。

So for example, if I put certain context:component-scan tags into configuration files that are below applicationContext.xml, it could cause certain classes to not get scanned. Things of that nature. I don't understand the precedence and what has to go where to be sure it's seen application wide and so on. If anyone can clearly explain it or point me to a resource that explains it I would much appreciate it, thank you. Hopefully what I'm asking makes sense.

因此,例如,如果我将某些 context:component-scan 标记放入 applicationContext.xml 下方的配置文件中,则可能会导致某些类无法被扫描。那种性质的东西。我不明白优先级以及必须去哪里才能确保它在广泛的应用程序中被看到等等。如果有人可以清楚地解释它或向我指出解释它的资源,我将不胜感激,谢谢。希望我问的是有道理的。

回答by Ryan Stewart

There's nothing special about the file named "applicationContext.xml" except that it's the name Spring tends to expect as its default configuration file. Using one file named that or multiple files named "dog.xml", "cat.xml", and "alien.xml" will work exactly the same way. The trouble you're having comes from having multiple ApplicationContexts in use at the same time, not from having multiple XML files. I've recently answered a couple of questions from people who had problems caused by not understanding these concepts. Check out those answers, and see what questions you still have:

名为“applicationContext.xml”的文件没有什么特别之处,只是 Spring 倾向于将其命名为默认配置文件。使用一个名为 that 的文件或多个名为“dog.xml”、“cat.xml”和“alien.xml”的文件的工作方式完全相同。您遇到的麻烦来自同时使用多个 ApplicationContext,而不是来自多个 XML 文件。我最近回答了一些因不理解这些概念而遇到问题的人的问题。查看这些答案,看看您还有哪些问题:

Declaring Spring Bean in Parent Context vs Child Context

在父上下文与子上下文中声明 Spring Bean

Spring-MVC: What are a "context" and "namespace"?

Spring-MVC:什么是“上下文”和“命名空间”?

Edit:In response to your new question:

编辑:回应你的新问题:

I had a <context:component-scan base-package="com.myapp"/>tag in my servlet.xml.

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

I'm guessing this "servlet.xml" file is named like foo-servlet.xml, where the DispatcherServlet configured in your web.xml is named "foo", like

我猜这个“servlet.xml”文件被命名为 like foo-servlet.xml,在你的 web.xml 中配置的 DispatcherServlet 被命名为“foo”,比如

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

By convention, when this DispatcherServlet starts, it'll create a new ApplicationContext that's configured by the file foo-servlet.xml, derived from the servlet-name. Now, since you put a context:component-scanin there, it's going to recursively scan the given package and create beans for all annotated classes. The package you gave it, com.myapp, looks like it's the base package for your entire app, so Spring will create beans from allof the annotated classes in your app, including the data access ones, in this one ApplicationContext that's associated to the DispatcherServlet. Typically, this context should only have view-layer stuff and beans that directly support the DispatcherServlet in it, so this was something of a misconfiguration.

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

In my data.xml file I had data source beans and that was it. No other beans, everything else was autowired and annotated.

在我的 data.xml 文件中,我有数据源 bean,仅此而已。没有其他 bean,其他所有内容都是自动装配和注释的。

Presumably, this "data.xml" file is the one you listed in the contextConfigLocationcontext-param. Assuming you'd also added the ContextLoaderListener to your web.xml, like

据推测,这个“data.xml”文件就是您在contextConfigLocation上下文参数中列出的文件。假设您还将 ContextLoaderListener 添加到您的web.xml,例如

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

then that file will be used to create a second ApplicationContext--the root context. That's what this listener does. Note that it actually builds the context from allthe files listed in contextConfigLocation, and if you also included your "servlet.xml" in that list, then you've loaded that config twice: here in the root context as well as in the context associated with the DipatcherServlet. Hopefully you see now how there's a distinct division between the XML configuration files and the ApplicationContexts that they configure. The same XML file can easily be used to configure two different contexts. Whether doing so is correct or not is another question. In this particular case, it isn't.

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

The order I've described these two contexts in is actually backwards. I was just following your description of what you did. The ContextLoaderListener, being a ServletContextListener, will always execute before any servlet starts up. This means the root context is created first, and the other context second. This is by design so that when the DispatcherServlet creates its context, it can add that context as a child of the root context. I've described this relationship in those other posts. The most important effect of this is that beans in the root context are available to and via the DispatcherServlet's context. That applies to autowired relationships, too. That's important because the DispatcherServlet onlylooks in its associated context for beans that it needs, like controller instances. Your controllers, though, obviously have to be wired with supporting beans. Thus, traditionally, the controllers live in the DispatcherServlet's context, and the supporting beans live in the root context.

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

I then tried to add @Transacational to my service bean and it wouldn't persist.

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

In order for @Transactional to work, you must include the <tx:annotation-driven/>tag in the configuration of the ApplicationContext where the annotated bean lives. The trick is figuring out the "where it lives" part. Beans in a child can override beans in a parent context. Therefore--I'm just guessing here--if you loaded all your beans into the DispatcherServlet context as I described above but put the <tx:annotation-driven/>in the root context, you might have a bean in the root context that's correctly transactional, but it's not the one being used because the duplicate is "closer" to the servlet in the parent/child hierarchy, and the context it's in didn't get a <tx:annotation-driven/>configuration.

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

When I changed the servlet context:component-scan tag to instead point at com.myapp.web and then added a context:component-scan tag to the data.xml file, everything worked.

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

It still depends somewhat on exactly which config files you were including in which ApplicationContexts, but at the very least I can say that by doing this, you removed a lot of beans from the DispatcherServlet's context which were causing problems. In particular, your correctly-configured @Transactional beans in the root context would no longer be shadowed by beans in the child context and would be injected into your controllers, so your persistence stuff would work then.

它仍然在某种程度上取决于您将哪些配置文件包含在哪些 ApplicationContext 中,但至少我可以说,通过这样做,您从 DispatcherServlet 的上下文中删除了许多导致问题的 bean。特别是,您在根上下文中正确配置的 @Transactional bean 将不再被子上下文中的 bean 遮蔽,并且会被注入到您的控制器中,因此您的持久性内容将起作用。

So... the main thing to take away is that you have two related ApplicationContexts. You have to remain aware of that fact and stay in control of which beans go in which context.

所以...要带走的主要事情是你有两个相关的 ApplicationContexts。您必须始终意识到这一事实,并控制哪些 bean 出现在哪个上下文中。

Does that cover everything?

这是否涵盖所有内容?