Справка / объяснение иерархии конфигурации файлов Spring XML
Когда я впервые начал изучать Spring, все было настроено в файле applicationContext.xml. Затем, когда я начал читать книги, посвященные более поздним версиям Spring, все они выполнили настройку в отдельных XML-файлах, таких как myapp-servlet-xml, myapp-security.xml, myapp-service.xml и т. Д., С помощью настройка contextConfigLocation в файле web.xml. Так, например, код, за которым я следовал, имел это как contextConfigLocation:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/myapp-servlet.xml
/WEB-INF/myapp-data.xml
</param-value>
</context-param>
Во всяком случае, недавно я столкнулся с проблемой конфигурации (которую помогли выяснить полезные люди из Stackru), которая возникла из-за этого разделения. Для примеров из этих книг не было файла applicationContext.xml, а позже, когда я попытался добавить автоматическое сканирование и аннотации в приложение, это вызвало проблемы. Я попытался переместить все в applicationContext.xml и покончить с другими файлами, и это решило проблему. Больше ничего не изменилось, я просто положил все в applicationContext.xml.
Таким образом, это, наряду с комментариями других, привело меня к некоторому пониманию того, что даже если вы не создадите applicationContext.xml, он все еще используется и является верхним уровнем иерархии конфигурации. Я надеюсь, что кто-то еще сможет объяснить мне, как это все работает, потому что я нигде не встречал никаких объяснений.
Так, например, если я добавлю определенный тег context: scan в файлы конфигурации, которые находятся ниже applicationContext.xml, это может привести к тому, что определенные классы не будут сканироваться. Вещи такой природы. Я не понимаю приоритета и что нужно делать, чтобы быть уверенным, что приложение широко используется и так далее. Если кто-то может четко объяснить это или указать мне ресурс, который объясняет это, я был бы очень признателен, спасибо. Надеюсь, то, что я спрашиваю, имеет смысл.
1 ответ
В файле с именем applicationContext.xml нет ничего особенного, за исключением того, что Spring ожидает его в качестве файла конфигурации по умолчанию. Использование одного файла с таким же именем или нескольких файлов с именами "dog.xml", "cat.xml" и "alien.xml" будет работать точно так же. Проблема возникает из-за одновременного использования нескольких ApplicationContexts, а не из-за наличия нескольких файлов 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
там он собирается рекурсивно сканировать данный пакет и создавать bean-компоненты для всех аннотированных классов. Пакет, который ты дал, com.myapp
Похоже, что это базовый пакет для всего вашего приложения, поэтому Spring создаст bean-компоненты из всех аннотированных классов в вашем приложении, включая классы доступа к данным, в этом одном ApplicationContext, который связан с DispatcherServlet. Как правило, в этом контексте должны содержаться только элементы уровня представления и компоненты, напрямую поддерживающие DispatcherServlet, так что это было неправильной конфигурацией.
В моем файле data.xml у меня были компоненты источника данных, и это было все. Никаких других бобов, все остальное было автоматически подключено и аннотировано.
Предположительно, этот файл "data.xml" - тот, который вы указали в contextConfigLocation
контекстно-пары. Предполагая, что вы также добавили 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 создает свой контекст, он может добавить этот контекст как дочерний элемент корневого контекста. Я описал эти отношения в других постах. Наиболее важным результатом этого является то, что бины в корневом контексте доступны для контекста DispatcherServlet и через него. Это относится и к отношениям с автоматической связью. Это важно, потому что DispatcherServlet ищет в связанном контексте только те компоненты, которые ему нужны, например, экземпляры контроллера. Однако ваши контроллеры, очевидно, должны быть подключены к поддерживающим компонентам. Таким образом, традиционно контроллеры живут в контексте DispatcherServlet, а поддерживающие bean-компоненты живут в корневом контексте.
Затем я попытался добавить @Transacational к своему служебному бину, и он не сохранился.
Чтобы @Transactional работал, вы должны включить <tx:annotation-driven/>
тег в конфигурации ApplicationContext, где живет аннотированный компонент. Хитрость заключается в том, чтобы выяснить, где он живет. Бины в дочернем элементе могут переопределять бины в родительском контексте. Поэтому - я просто догадываюсь - если вы загрузили все свои bean-компоненты в контекст DispatcherServlet, как я описал выше, но поставили <tx:annotation-driven/>
в корневом контексте у вас может быть bean-компонент в корневом контексте, который является корректно транзакционным, но это не тот, который используется, потому что дубликат "ближе" к сервлету в иерархии "родитель / потомок", а контекст, в котором он находится, отсутствует получить <tx:annotation-driven/>
конфигурации.
Когда я изменил тег контекста сервлета: компонент-сканирование, чтобы вместо этого указать на com.myapp.web, а затем добавил тег контекста: компонент-сканирование в файл data.xml, все работало.
Это все еще в некоторой степени зависит от того, какие именно файлы конфигурации вы включали в какие ApplicationContexts, но, по крайней мере, я могу сказать, что, выполнив это, вы удалили много компонентов из контекста DispatcherServlet, которые вызывали проблемы. В частности, ваши правильно сконфигурированные bean-компоненты @Transactional в корневом контексте больше не будут скрыты bean-компонентами в дочернем контексте и будут внедрены в ваши контроллеры, так что ваши персистентные вещи будут работать тогда.
Итак... главное, что нужно убрать, это то, что у вас есть два связанных ApplicationContexts. Вы должны осознавать этот факт и контролировать, какие бины и в каком контексте.
Это охватывает все?