Рубрики
Без рубрики

Несколько иерархических контекстов в Spring Boot

TL; ДР В дополнение к созданию только одного ApplicationContext в типичной загрузке Spring… С тегами springframework, spring boot, java, maven.

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 , содержащее несколько контекстов, и как они могут формировать отношения между родителями и детьми.

Мы также упомянули пару реальных сценариев, в которых мы могли бы структурировать контексты в иерархии и как это сделать.

Кроме того, мы видели, что существует множество способов формирования отношений между родителями и детьми, включая отношения между братьями и сестрами.

Рекомендации

Оригинал: “https://dev.to/__nikolamicic21/multiple-hierarchical-contexts-in-spring-boot-25nf”