问题描述
当我第一次开始学习Spring时,事情是在applicationContext.xml文件中配置的.然后,当我开始专门阅读有关Spring的最新版本的书籍时,他们都通过单独的XML文件(例如myapp-servlet-xml,myapp-security.xml,myapp-service.xml等)完成了配置.在web.xml文件中配置contextConfigLocation.因此,例如,我一直遵循的代码将其作为contextConfigLocation:
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:
<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中.
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.
因此,这以及其他人的评论使我有点理解,即使您不创建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.
例如,如果我将某些context:component-scan标记放入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.
推荐答案
名为"applicationContext.xml"的文件没有什么特别之处,只是它是Spring倾向于将其用作默认配置文件的名称.使用一个名为该文件的文件或多个名为"dog.xml","cat.xml"和"alien.xml"的文件将完全相同.您遇到的麻烦来自同时使用多个ApplicationContext,而不是来自多个XML文件.我最近回答了一些人的问题,这些人因不了解这些概念而遇到问题.查看这些答案,看看您还有什么问题:
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:
修改:针对您的新问题:
我猜这个"servlet.xml"文件的名称类似于foo-servlet.xml,其中在web.xml中配置的DispatcherServlet的名称是"foo",例如
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> <servlet-name>foo</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> </servlet>
按照惯例,当此DispatcherServlet启动时,它将创建一个新的ApplicationContext,该文件由servlet-name派生的文件foo-servlet.xml配置.现在,由于您在其中放置了context:component-scan,因此它将递归地扫描给定的包并为所有带注释的类创建bean.您提供给它的包com.myapp看起来像是整个应用程序的基础包,因此Spring将从应用程序中带注释的类的 all 中创建bean,包括数据访问类.与DispatcherServlet关联的这一ApplicationContext.通常,此上下文中应只包含直接支持DispatcherServlet的视图层内容和bean,因此这是一种错误的配置.
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-scan in 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 all of 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.
大概,此"data.xml"文件是您在contextConfigLocation上下文参数中列出的文件.假设您还已将ContextLoaderListener添加到您的web.xml中,例如
Presumably, this "data.xml" file is the one you listed in the contextConfigLocation context-param. Assuming you'd also added the ContextLoaderListener to your web.xml, like
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
然后该文件将用于创建第二个ApplicationContext-根上下文.那就是这个听众所做的.请注意,它实际上是根据中列出的所有文件构建 all 上下文的,如果您还在该列表中包含"servlet.xml",则该配置已加载两次:根上下文以及与DipatcherServlet关联的上下文.希望您现在看到XML配置文件和它们配置的ApplicationContext之间如何有明显的区分.相同的XML文件可以轻松地用于配置两个不同的上下文.这样做是否正确是另一个问题.在这种情况下,不是.
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 all the 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.
我描述这两个上下文的顺序实际上是倒序的.我只是在听你对你所做的事情的描述.作为 ServletContextListener 的ContextLoaderListener将始终在执行之前任何servlet都会启动.这意味着首先创建根上下文,然后创建另一个上下文.这是设计使然,以便DispatcherServlet创建其上下文时,可以将该上下文添加为根上下文的子级.我已经在其他文章中描述了这种关系.这样做的最重要的效果是,根上下文中的bean可用于DispatcherServlet的上下文,也可以通过DispatcherServlet的上下文使用.这也适用于自动装配的关系.这很重要,因为DispatcherServlet仅 在其关联上下文中查找所需的bean,例如控制器实例.但是,您的控制器显然必须与支持bean连线.因此,传统上,控制器位于DispatcherServlet的上下文中,而支持bean则位于根上下文中.
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 only looks 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.
为了使@Transactional工作,必须在带注释的Bean所在的ApplicationContext的配置中包括<tx:annotation-driven/>标记.诀窍是弄清楚它住的地方"部分.子级中的Bean可以覆盖父级上下文中的Bean.因此-我只是在这里猜测-如果您如上所述将所有bean都加载到DispatcherServlet上下文中,但是将<tx:annotation-driven/>放在根上下文中,则可能在根上下文中有一个正确进行事务处理的bean,但是它不是正在使用的那个,因为副本与父/子层次结构中的servlet更近",并且它所在的上下文没有得到<tx:annotation-driven/>配置.
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.
这仍然在一定程度上取决于您在哪个ApplicationContext中包含了哪些配置文件,但是至少我可以说,通过这样做,您从DispatcherServlet的上下文中删除了很多导致问题的bean.特别是,在根上下文中正确配置的@Transactional Bean将不再被子上下文中的Bean所遮蔽,并且将被注入到控制器中,因此您的持久性工作将可以正常工作.
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.您必须保持对这一事实的了解,并控制在哪种上下文中使用哪种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.
涵盖所有内容吗?
这篇关于Spring XML文件配置层次结构帮助/说明的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!