Перейти к основному содержимому

Иерархия контекста с API Spring Boot Fluent Builder

· 4 мин. чтения

1. Обзор

В Spring Boot можно создавать отдельные контексты и организовывать их в иерархию.

Иерархия контекста может быть определена по-разному в приложении Spring Boot. В этой статье мы рассмотрим, как мы можем создавать несколько контекстов с помощью API-интерфейса Fluent Builder .

Поскольку мы не будем вдаваться в подробности о том, как настроить приложение Spring Boot, вы можете прочитать эту статью .

2. Иерархия контекста приложения

У нас может быть несколько контекстов приложения, которые разделяют отношение родитель-потомок .

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

Кроме того, мы можем использовать контексты для предотвращения доступа к bean-компонентам, зарегистрированным в одном контексте, в другом. Это облегчает создание слабосвязанных модулей.

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

3. Использование API SpringApplicationBuilder

Класс SpringApplicationBuilder предоставляет гибкий API для создания отношений родитель-потомок между контекстами с использованием методов parent() , child() и sibling() .

Чтобы проиллюстрировать иерархию контекста, мы настроим не веб-контекст родительского приложения с двумя дочерними веб-контекстами.

Чтобы продемонстрировать это, мы запустим два экземпляра встроенного Tomcat, каждый со своим собственным контекстом веб-приложения, и оба будут работать в одной JVM.

3.1. Родительский контекст

Для начала давайте создадим сервисный компонент вместе с классом определения компонента, который находится в родительском пакете. Мы хотим, чтобы этот компонент возвращал приветствие, которое отображается клиенту нашего веб-приложения:

@Service
public class HomeService implements IHomeService {

public String getGreeting() {
return "Welcome User";
}
}

И класс определения компонента:

@Configuration
@ComponentScan("com.foreach.parent")
public class ServiceConfig {}

Далее мы создадим конфигурацию для двух дочерних контекстов.

3.2. Дочерний контекст

Поскольку все контексты настраиваются с использованием файла конфигурации по умолчанию, нам необходимо предоставить отдельные конфигурации для свойств, которые не могут быть общими для контекстов, таких как порты сервера.

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

Начнем с определения файла свойств для первого дочернего контекста:

server.port=8074
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.foreach.ctx1")
@EnableAutoConfiguration
public class Ctx1Config {

@Bean
public IHomeService homeService() {
return new GreetingService();
}
}

Этот класс предоставляет новое определение для bean- компонента homeService , которое перезапишет определение из родителя.

Давайте посмотрим на определение класса GreetingService :

@Service
public class GreetingService implements IHomeService {

public String getGreeting() {
return "Greetings for the day";
}
}

Наконец, мы добавим контроллер для этого веб-контекста, который использует bean- компонент homeService для отображения сообщения пользователю:

@RestController
public class Ctx1Controller {

@Autowired
private HomeService homeService;

@GetMapping("/home")
public String greeting() {
return homeService.getGreeting();
}
}

3.3. Родственный контекст

Для нашего второго контекста мы создадим контроллер и класс конфигурации, которые очень похожи на те, что были в предыдущем разделе.

На этот раз мы не будем создавать bean-компонент homeService , так как мы будем обращаться к нему из родительского контекста.

Во-первых, давайте добавим файл свойств для этого контекста:

server.port=8075
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.foreach.ctx2")
@EnableAutoConfiguration
@PropertySource("classpath:ctx2.properties")
public class Ctx2Config {}

Давайте также добавим контроллер, который имеет HomeService в качестве зависимости:

@RestController
public class Ctx2Controller {

@Autowired
private IHomeService homeService;

@GetMapping("/greeting")
public String getGreeting() {
return homeService.getGreeting();
}
}

В этом случае наш контроллер должен получить bean- компонент homeService из родительского контекста.

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:8074/ctx1/home и localhost:8075/ctx2/greeting.

4. Вывод

Используя API SpringApplicationBuilder , мы сначала создали отношение родитель-потомок между двумя контекстами приложения. Далее мы рассмотрели, как переопределить родительскую конфигурацию в дочернем контексте. Наконец, мы добавили одноуровневый контекст, чтобы продемонстрировать, как конфигурация в родительском контексте может использоваться совместно с другими дочерними контекстами.

Исходный код примера доступен на GitHub .