Ответ 1
Нет ничего особенного в файле с именем "applicationContext.xml", за исключением того, что его имя Spring имеет тенденцию ожидать в качестве файла конфигурации по умолчанию. Используя один файл с именем, который или несколько файлов с именем "dog.xml", "cat.xml" и "alien.xml" будут работать точно так же. Проблема, с которой вы столкнулись, связана с одновременным использованием нескольких ApplicationContext, а не с несколькими XML файлами. Недавно я ответил на несколько вопросов от людей, у которых были проблемы, вызванные не пониманием этих понятий. Просмотрите эти ответы и посмотрите, какие у вас есть вопросы:
Объявление Spring Bean в контексте родительского контекста и дочернего контекста
Spring -MVC: что такое контекст " и "пространство имен" ,
Изменить: В ответ на ваш новый вопрос:
У меня был тег
<context:component-scan base-package="com.myapp"/>
в моем файле servlet.xml.
Я предполагаю, что этот файл "servlet.xml" имеет имя как foo-servlet.xml
, где DispatcherServlet, настроенный в вашем web.xml, называется "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
, он будет рекурсивно проверять данный пакет и создавать beans для всех аннотированных классов. Пакет, который вы ему дали, com.myapp
, выглядит как базовый пакет для всего вашего приложения, поэтому Spring создаст beans из всех аннотированных классов в вашем приложении, включая данные, имеющие доступ к данным, в этом приложении ApplicationContext связанных с диспетчером. Как правило, этот контекст должен иметь только материал уровня представления и beans, которые непосредственно поддерживают DispatcherServlet в нем, поэтому это было что-то неправильное.
В моем файле data.xml у меня был источник данных beans, и все. Никакой другой beans, все остальное было автообновлено и аннотировано.
Предположительно, этот файл "data.xml" является тем, который вы указали в контексте contextConfigLocation
context-param. Предполагая, что вы также добавили ContextLoaderListener к вашему web.xml
, например
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
то этот файл будет использоваться для создания второго ApplicationContext - корневого контекста. Это то, что делает этот слушатель. Обратите внимание, что он фактически создает контекст из всех файлов, перечисленных в contextConfigLocation
, и если вы также включили свой "servlet.xml" в этот список, то вы дважды загрузили эту конфигурацию: здесь, в корневом контексте, а также в контекст, связанный с DipatcherServlet. Надеемся, теперь вы увидите, как существует четкое разделение между файлами конфигурации XML и настраиваемыми ApplicationContexts. Один и тот же файл XML можно легко использовать для настройки двух разных контекстов. Правильно ли это или нет, это другой вопрос. В этом конкретном случае это не так.
Порядок, который я описал в этих двух контекстах, фактически обратный. Я просто следил за вашим описанием того, что вы сделали. ContextLoaderListener, будучи ServletContextListener, всегда будет выполняться до запуска любого сервлета. Это означает, что сначала создается корневой контекст, а второй - второй. Это по дизайну, так что, когда DispatcherServlet создает свой контекст, он может добавить этот контекст в качестве дочернего элемента корневого контекста. Я описал эти отношения в этих других сообщениях. Наиболее важным эффектом этого является то, что beans в корневом контексте доступен и через контекст DispatcherServlet. Это относится и к автономным отношениям. Это важно, потому что DispatcherServlet только ищет в своем контексте для beans, что ему нужно, например, экземпляры контроллера. Разумеется, ваши контроллеры должны быть подключены с поддержкой beans. Таким образом, традиционно контроллеры живут в контексте DispatcherServlet, а поддерживающий beans - в корневом контексте.
Затем я попытался добавить @Transacational к моему сервису Bean, и он не будет сохраняться.
Чтобы работать @Transactional, вы должны включить тег <tx:annotation-driven/>
в конфигурацию ApplicationContext, где проживает аннотированный Bean. Хитрость заключается в том, чтобы выяснить, "где он живет". beans у ребенка может переопределить beans в родительском контексте. Поэтому - я просто угадываю здесь - если вы загрузили весь свой beans в контекст DispatcherServlet, как описано выше, но поместите <tx:annotation-driven/>
в корневой контекст, вы можете иметь Bean в корневом контексте, который корректно транзакционный, но это не тот, который используется, потому что дубликат "ближе" к сервлету в родительской/дочерней иерархии, а контекст, в котором он не получил конфигурацию <tx:annotation-driven/>
.
Когда я изменил контекст сервлета: тег компонента-проверки, чтобы вместо этого указать com.myapp.web, а затем добавил в файл data.xml тег контекста: компонент-сканирование, все сработало.
По-прежнему зависит от того, какие именно файлы конфигурации вы использовали в ApplicationContexts, но, по крайней мере, я могу сказать, что, выполнив это, вы удалили много beans из контекста DispatcherServlet, которые вызывали проблемы. В частности, ваш правильно сконфигурированный @Transactional beans в корневом контексте больше не будет затенен beans в дочернем контексте и будет введен в ваши контроллеры, поэтому ваш материал сохранения будет работать тогда.
Итак... главное, чтобы убрать, что у вас есть два связанных ApplicationContexts. Вы должны оставаться в курсе этого факта и контролировать, в каком контексте beans идти в этом контексте.
Все это покрывает?