TL; ДР
В дополнение к созданию только одного ApplicationContext
в типичном весеннем загрузочном приложении есть возможность создать несколько экземпляров Контекст приложения
в одном приложении. Эти контексты могут формировать отношения между родителями и детьми между ними. Таким образом, у нас больше нет ни одного ApplicationContext
содержащий все компоненты , определенные в приложении. Мы скорее можем иметь иерархию контекстов, каждый из которых содержит компоненты , определенные внутри самих себя. Формируя отношения родитель-потомок между контекстами, можно управлять доступностью компонентов , определенных в контексте.
Вступление
В типичном Весеннем загрузочном приложении, написанном в Java , наш основной метод будет выглядеть так же, как показано ниже.
@SpringBootApplication public class MainApplication { public static void main(String[] args) { SpringApplication.run(MainApplication.class, args); } }
Статический метод запуск
из Приложения Spring/| класс создаст единый
ApplicationContext . Контекст будет содержать все пользовательские
компоненты , определенные в приложении, а также компоненты , которые будут созданы в процессе автоматической настройки Spring . Это означает, что мы можем запросить автоматическое подключение любого компонента из контекста с помощью механизма внедрения зависимостей Spring в любом месте нашего приложения. В большинстве случаев это будет предпочтительным поведением.
Однако могут возникнуть ситуации, когда мы не хотим, чтобы все определенные компоненты были доступны во всем приложении. Мы могли бы ограничить доступ к некоторым конкретным компонентам , потому что часть приложения не должна знать о них. Реальным примером может быть то, что у нас есть многомодульный проект, в котором мы не хотим, чтобы компоненты , определенные в одном модуле, знали о компонентах , определенных в других модулях. Другим примером может быть то, что в типичной 3-слойной архитектуре (контроллер-сервис-репозиторий) мы хотели бы ограничить доступ к контроллеру beans из репозитория или связанного с сервисом beans .
Spring Boot позволяет создавать несколько контекстов с помощью своего API-интерфейса Fluent Builder с основным классом SpringApplicationBuilder
в этом API.
Приложение с одним контекстом
Мы начнем с простого Spring Boot приложения, имеющего только один контекст. Заявление будет написано в Java и будет использовать Maven в качестве инструмента построения.
Приложение будет содержать только ядро Spring и Весенняя загрузка зависимости с помощью spring-boot-starter . В результате pom.xml
файл должен выглядеть так, как указано в ссылке .
В приложении есть два разных пакета
- веб пакет
- данные пакет
оба содержат соответствующие бобы ( Веб-сервис
и Служба передачи данных
).
Исходный код приложения с одним контекстом, в котором Веб-служба
зависит от Службы данных
, можно найти здесь .
Если мы заглянем в метод main и извлекем контекст из приложения Spring
, мы можем запросить бобы в контексте, как в приведенном ниже фрагменте кода.
@SpringBootApplication public class MainApplication { public static void main(String[] args) { final var context = SpringApplication.run(MainApplication.class, args); final var webService = context.getBean(WebService.class); final var dataService = context.getBean(DataService.class); } }
Spring разрешит оба компонента и вернет одноэлементные экземпляры (по умолчанию) для обоих классов ( Веб-сервис
и Служба передачи данных
). Это означает, что оба компонента доступны из контекста с помощью метода getBean и Spring может автоматически подключать экземпляр Службы данных
к экземпляру Веб-службы
с помощью механизма внедрения зависимостей.
Мы могли бы достичь обратного сценария, когда Служба передачи данных
зависит от Веб-сервис
. Весна сможет автоматически подключать бобы типа Веб-сервис
в Службу передачи данных
фасоль .
Исходный код с обратным направлением зависимостей можно найти здесь .
Все это возможно, потому что все бобы являются частью одного и того же контекста .
Несколько иерархических контекстов
С помощью SpringApplicationBuilder мы можем создать несколько контекстов в нашем приложении и сформировать отношения между родителями и детьми между ними. Таким образом, мы можем ограничить доступ к Веб-сервис
|был таким образом, что он не может быть введен в
Службу данных
Правило для иерархии с несколькими контекстами с отношениями родитель-потомок состоит в том, что компоненты из родительского контекста доступны в дочерних контекстах , но не наоборот. В нашем случае контекст, который определяет Веб-служба
будет дочерним контекстом контекста, который определяет Служба передачи данных
. Другое правило заключается в том, что контекст может иметь только один родительский контекст .
Этого можно было бы достичь за пару шагов
- Первым шагом будет создание контекста для связанных с вебом компонентов в новом классе с аннотацией
@Configuration
в веб-пакете. Этот контекст будет сканировать все компоненты , определенные в веб-пакете, с помощью@ComponentScan
. Аннотация@EnableAutoConfiguration
позаботится о запуске Spring процесс автоматической настройки.
@ComponentScan @EnableAutoConfiguration @Configuration(proxyBeanMethods = false) public class WebContextConfiguration { }
- Вторым шагом будет создание контекста для связанных с данными компонентов в новом классе с аннотацией
@Configuration
в пакете данных. Компоненты , определенные в пакете данных, будут обнаружены так же, как и те, которые определены в веб-пакете.
@ComponentScan @EnableAutoConfiguration @Configuration(proxyBeanMethods = false) public class DataContextConfiguration { }
Третий шаг – использовать только
@SpringBootConfiguration
аннотацию в нашем основном классе вместо хорошо известной@SpringBootApplication
аннотации, чтобы предотвратить сканирование компонентов и автоматическую настройку. Как мы видели ранее, оба процесса (сканирование компонентов и автоматическая настройка) будут выполняться в каждом дочернем контексте отдельно.Шаг четвертый – использовать
SpringApplicationBuilder
класс вместоSpring Application
класса, как в приведенном ниже фрагменте кода. Метод web вSpringApplicationBuilder
указывает, какой тип веб-приложения будет определяться конкретным контекстом (возможные значения: НЕТ, СЕРВЛЕТ и РЕАКТИВНЫЙ, определенные вWebApplicationType
).
@SpringBootConfiguration public class MainApplication { public static void main(String[] args) { new SpringApplicationBuilder() .sources(MainApplication.class) .web(NONE) .child(DataContextConfiguration.class) .web(NONE) .child(WebContextConfiguration.class) .web(NONE) .run(args); } }
Иерархия контекста, определенная выше в SpringApplicationBuilder
, может быть представлена следующей диаграммой.
Если мы сейчас попытаемся провести автоматическое подключение Веб-сервис
в Сервис передачи данных
боб мы получим Исключение NoSuchBeanDefinitionException
заявляющее, что Веб-сервис
боб не может быть найден. Это происходит из утверждения, что компоненты , определенные в дочерних контекстах, недоступны из родительского контекста. Исходный код с этими изменениями можно найти здесь .
Не будет никаких исключений в случае, когда мы попытаемся автоматически подключить Службу передачи данных
в Веб-службу
как бобы , определенные в родительском контексте, видны бобам , определенным в дочерних контекстах. Исходный код приложения с несколькими контекстами, где Веб-сервис
зависит от Службы передачи данных
можно найти здесь .
Иерархия контекста родитель-ребенок-брат или сестра
Еще одна интересная иерархия, которую можно применить, – это когда у нас есть один родительский контекст с несколькими дочерними контекстами, где каждый дочерний контекст формирует родственные отношения с другими дочерними контекстами.
Правило для родственных контекстов состоит в том, что компоненты в одном родственном контексте не могут получить доступ к компонентам , определенным в других родственных контекстах .
Давайте посмотрим, как выглядит наш основной класс в этом случае.
@SpringBootConfiguration public class MainApplication { public static void main(String[] args) { new SpringApplicationBuilder() .sources(MainApplication.class) .web(NONE) .child(DataContextConfiguration.class) .web(NONE) .sibling(EventContextConfiguration.class) .web(NONE) .sibling(WebContextConfiguration.class) .web(NONE) .run(args); } }
Как мы видим, мы ввели дополнительный контекст Конфигурация контекста события
в отдельном пакете событие . Контекст определяется таким же образом, как и другие дочерние контексты.
@ComponentScan @EnableAutoConfiguration @Configuration(proxyBeanMethods = false) public class EventContextConfiguration { }
Схема такого рода иерархии контекстов показана ниже.
Как мы можем видеть из диаграммы, все дочерние контексты имеют одного и того же родителя и формируют родственные отношения.
Если мы сохраним ту же иерархию зависимостей, где Веб-сервис
зависит от Службы передачи данных
, мы получили бы исключение NoSuchBeanDefinitionException
исключение, потому что оно недоступно из родственного контекста. Исходный код для этого этапа приложения можно найти здесь .
Следует отметить, что дочерние контексты все еще могут получать доступ к компонентам , определенным в родительском контексте.
Резюме
В этой части мы рассмотрели, как мы можем создать приложение Spring Boot , содержащее несколько контекстов, и как они могут формировать отношения между родителями и детьми.
Мы также упомянули пару реальных сценариев, в которых мы могли бы структурировать контексты в иерархии и как это сделать.
Кроме того, мы видели, что существует множество способов формирования отношений между родителями и детьми, включая отношения между братьями и сестрами.
Рекомендации
- Справочная документация по весенней загрузке – API Fluent Builder
- Доменный дизайн на примерах – Библиотечный проект
- Иерархия контекста с помощью API Spring Boot Fluent Builder
Оригинал: “https://dev.to/__nikolamicic21/multiple-hierarchical-contexts-in-spring-boot-25nf”