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

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

1. обзор

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

Контекстная иерархия может быть определена различными способами в приложении Spring Boot. В этой статье мы рассмотримhow we can create multiple contexts using the fluent builder API.

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

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

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

A context hierarchy allows multiple child contexts to share beans which reside in the parent context. Каждый дочерний контекст может переопределять конфигурацию, унаследованную от родительского контекста.

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

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

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

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

Чтобы проиллюстрировать контекстную иерархию,we’ll set up a non-web parent application context with 2 child web contexts.

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

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

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

@Service
public class HomeService implements IHomeService {

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

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

@Configuration
@ComponentScan("com.example.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.example.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 для отображения сообщения пользователю:

@Controller
public class Ctx1Controller {

    @Autowired
    HomeService homeService;

    @GetMapping("/home")
    @ResponseBody
    public String greeting() {

        return homeService.getGreeting();
    }
}

3.3. Братский Контекст

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

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

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

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.example.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();
    }
}

В этом случае наш контроллер должен получить 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:8081/ctx1/home andlocalhost:8082/ctx2/greeting.

4. Заключение

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

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