1. Обзор
В Spring Boot можно создавать отдельные контексты и организовывать их в иерархию.
Иерархия контекста может быть определена различными способами в приложении Spring Boot. В этой статье мы рассмотрим , как мы можем создавать несколько контекстов с помощью API fluent builder .
Поскольку мы не будем вдаваться в подробности о том, как настроить приложение Spring Boot, вы можете ознакомиться с этой статьей .
2. Иерархия контекста приложения
У нас может быть несколько контекстов приложений, которые разделяют отношения между родителями и детьми .
Иерархия контекста позволяет нескольким дочерним контекстам совместно использовать компоненты, находящиеся в родительском контексте. Каждый дочерний контекст может переопределять конфигурацию, унаследованную от родительского контекста.
Кроме того, мы можем использовать контексты для предотвращения доступа к бобам, зарегистрированным в одном контексте, в другом. Это облегчает создание слабо связанных модулей.
Здесь стоит отметить, что контекст может иметь только одного родителя, в то время как родительский контекст может иметь несколько дочерних контекстов. Кроме того, дочерний контекст может получить доступ к компонентам в родительском контексте, но не наоборот.
3. Использование API SpringApplicationBuilder
Класс SpringApplicationBuilder предоставляет свободный API для создания отношений родитель-потомок между контекстами с использованием методов parent() , child() и sibling () .
Чтобы проиллюстрировать иерархию контекстов, мы настроим не веб-родительский контекст приложения с 2 дочерними веб-контекстами.
Чтобы продемонстрировать это, мы запустим два экземпляра встроенного Tomcat, каждый из которых имеет свой собственный webapplicationcontext и оба работают в одной JVM.
3.1. Родительский контекст
Для начала давайте создадим компонент службы вместе с классом определения компонента, который находится в родительском пакете. Мы хотим, чтобы этот компонент возвращал приветствие, которое отображается клиенту нашего веб-приложения:
@Service public class HomeService implements IHomeService { public String getGreeting() { return "Welcome User"; } }
И класс определения бобов:
@Configuration @ComponentScan("com.baeldung.parent") public class ServiceConfig {}
Затем мы создадим конфигурацию для двух дочерних контекстов.
3.2. Контекст ребенка
Поскольку все контексты настраиваются с использованием файла конфигурации по умолчанию, нам необходимо предоставить отдельные конфигурации для свойств, которые не могут быть разделены между контекстами, такими как порты сервера.
Чтобы предотвратить обнаружение конфликтующих конфигураций автоматической конфигурацией, мы также будем хранить классы в отдельных пакетах.
Давайте начнем с определения файла свойств для первого дочернего контекста:
server.port=8081 server.servlet.context-path=/ctx1 spring.application.admin.enabled=false spring.application.admin.jmx-name=org.springframework.boot:type=Ctx1Rest,name=Ctx1Application
Обратите внимание, что мы настроили порт и контекстный путь, а также имя JMX, чтобы имена приложений не конфликтовали.
Теперь давайте добавим основной класс конфигурации для этого контекста:
@Configuration @ComponentScan("com.baeldung.ctx1") @EnableAutoConfiguration public class Ctx1Config { @Bean public IHomeService homeService() { return new GreetingService(); } }
Этот класс предоставляет новое определение для компонента home Service , который будет перезаписан из родительского компонента.
Давайте посмотрим на определение класса GreetingService :
@Service public class GreetingService implements IHomeService { public String getGreeting() { return "Greetings for the day"; } }
Наконец, мы добавим контроллер для этого веб-контекста, который использует компонент home Service для отображения сообщения пользователю:
@Controller public class Ctx1Controller { @Autowired HomeService homeService; @GetMapping("/home") @ResponseBody public String greeting() { return homeService.getGreeting(); } }
3.3. Родственный контекст
Для нашего второго контекста мы создадим контроллер и класс конфигурации, которые очень похожи на те, что были в предыдущем разделе.
На этот раз мы не будем создавать homeService bean, так как мы будем обращаться к нему из родительского контекста.
Во-первых, давайте добавим файл свойств для этого контекста:
server.port=8082 server.servlet.context-path=/ctx2 spring.application.admin.enabled=false spring.application.admin.jmx-name=org.springframework.boot:type=WebAdmin,name=SpringWebApplication
И класс конфигурации для родственного приложения:
@Configuration @ComponentScan("com.baeldung.ctx2") @EnableAutoConfiguration @PropertySource("classpath:ctx2.properties") public class Ctx2Config {}
Давайте также добавим контроллер, который имеет HomeService в качестве зависимости:
@RestController public class Ctx2Controller { @Autowired IHomeService homeService; @GetMapping(value = "/greeting", produces = "application/json") public String getGreeting() { return homeService.getGreeting(); } }
В этом случае наш контроллер должен получить home Service bean из родительского контекста.
3.4. Иерархия контекста
Теперь мы можем собрать все вместе и определить иерархию контекста с помощью SpringApplicationBuilder:
public class App { public static void main(String[] args) { new SpringApplicationBuilder() .parent(ParentConfig.class).web(WebApplicationType.NONE) .child(WebConfig.class).web(WebApplicationType.SERVLET) .sibling(RestConfig.class).web(WebApplicationType.SERVLET) .run(args); } }
Наконец, при запуске приложения Spring Boot мы можем получить доступ к обоим приложениям на их соответствующих портах с помощью localhost:8081/ctx1/home и localhost:8082/ctx2/приветствие.
4. Заключение
Использование SpringApplicationBuilder API, мы сначала создали отношения “родитель-потомок” между двумя контекстами приложения. Далее мы рассмотрели, как переопределить родительскую конфигурацию в дочернем контексте. Наконец, мы добавили контекст брата и сестры, чтобы продемонстрировать, как конфигурация в родительском контексте может быть совместно использована с другими дочерними контекстами.
Исходный код примера доступен на GitHub .