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>

Во всяком случае, в последнее время я столкнулся с проблемой конфигурации (которую помогли мне люди из StackOverflow), что было связано с этим разделением. Для примеров из этих книг не было файла applicationContext.xml, а позже, когда я попытался добавить автоматическое сканирование и аннотации к приложению, это вызвало проблемы. Я попытался переместить все в applicationContext.xml и покончить с другими файлами, и это решило проблему. Ничего другого не изменилось, я просто поместил все в applicationContext.xml.

Итак, это, наряду с комментариями других, привело ко мне понять, что даже если вы не создаете applicationContext.xml, он все еще используется и является верхним уровнем какой-либо иерархии конфигурации. Я надеюсь, что кто-то еще может объяснить мне, как все это работает, потому что я нигде не встречал никаких объяснений.

Так, например, если я помещаю определенный контекст: теги компонентного сканирования в файлы конфигурации, которые ниже applicationContext.xml, это может привести к тому, что определенные классы не будут отсканированы. Вещи такого характера. Я не понимаю приоритет и что нужно делать, чтобы убедиться, что он видел приложение широко и так далее. Если кто-нибудь может ясно объяснить это или указать мне на ресурс, который объясняет это, я был бы очень благодарен вам, спасибо. Надеюсь, то, что я прошу, имеет смысл.

Ответ 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 идти в этом контексте.

Все это покрывает?