Hierarquia de contexto com a API Spring Boot Fluent Builder

Hierarquia de contexto com a API Spring Boot Fluent Builder

1. Visão geral

É possível criar contextos separados e organizá-los em uma hierarquia no Spring Boot.

Uma hierarquia de contexto pode ser definida de diferentes maneiras no aplicativo Spring Boot. Neste artigo, veremoshow we can create multiple contexts using the fluent builder API.

Como não entraremos em detalhes sobre como configurar um aplicativo Spring Boot, você pode querer verificar estearticle.

2. Hierarquia de contexto do aplicativo

Podemos ter vários contextos de aplicativos que compartilham um relacionamento pai-filho.

A context hierarchy allows multiple child contexts to share beans which reside in the parent context. Cada contexto filho pode substituir a configuração herdada do contexto pai.

Além disso, podemos usar contextos para impedir que beans registrados em um contexto sejam acessíveis em outro. Isso facilita a criação de módulos fracamente acoplados.

Aqui, alguns pontos dignos de nota são que um contexto pode ter apenas um pai, enquanto um contexto pai pode ter vários contextos filhos. Além disso, um contexto filho pode acessar beans no contexto pai, mas não vice-versa.

3. Usando APISpringApplicationBuilder

A classeSpringApplicationBuilder fornece uma API fluente para criar um relacionamento pai-filho entre contextos usando os métodosparent(),child() andsibling().

Para exemplificar a hierarquia de contexto,we’ll set up a non-web parent application context with 2 child web contexts.

Para demonstrar isso, iniciaremos duas instâncias do Tomcat incorporado, cada uma com seu próprio contexto de aplicativo da web e ambas em execução em uma única JVM.

3.1. Contexto pai

Para começar, vamos criar um bean de serviço junto com uma classe de definição de bean que reside no pacote pai. Queremos que esse bean retorne uma saudação exibida ao cliente do nosso aplicativo da web:

@Service
public class HomeService implements IHomeService {

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

E a classe de definição de bean:

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

A seguir, vamos criar a configuração para os dois contextos filho.

3.2. Contexto infantil

Como todos os contextos são configurados usando o arquivo de configuração padrão, precisamos fornecer configurações separadas para propriedades que não podem ser compartilhadas entre contextos, como portas do servidor.

Para evitar que configurações conflitantes sejam capturadas pela configuração automática, também manteremos as classes em pacotes separados.

Vamos começar definindo um arquivo de propriedades para o primeiro contexto filho:

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

Observe que configuramos a porta e o caminho do contexto, bem como um nome JMX para que os nomes dos aplicativos não entrem em conflito.

Agora vamos adicionar a classe de configuração principal para este contexto:

@Configuration
@ComponentScan("com.example.ctx1")
@EnableAutoConfiguration
public class Ctx1Config {

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

Esta classe fornece uma nova definição para o beanhomeService que sobrescreverá o do pai.

Vamos ver a definição da classeGreetingService:

@Service
public class GreetingService implements IHomeService {

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

Por fim, adicionaremos um controlador para este contexto da web que usa o beanhomeService para exibir uma mensagem ao usuário:

@Controller
public class Ctx1Controller {

    @Autowired
    HomeService homeService;

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

        return homeService.getGreeting();
    }
}

3.3. Contexto de irmãos

Para o nosso segundo contexto, vamos criar um controlador e uma classe de configuração que são muito semelhantes aos da seção anterior.

Desta vez, não criaremos um beanhomeService - já que o acessaremos a partir do contexto pai.

Primeiro, vamos adicionar um arquivo de propriedades para este contexto:

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

E a classe de configuração para o aplicativo irmão:

@Configuration
@ComponentScan("com.example.ctx2")
@EnableAutoConfiguration
@PropertySource("classpath:ctx2.properties")
public class Ctx2Config {}

Vamos também adicionar um controlador, que temHomeService como dependência:

@RestController
public class Ctx2Controller {

    @Autowired
    IHomeService homeService;

    @GetMapping(value = "/greeting", produces = "application/json")
    public String getGreeting() {
        return homeService.getGreeting();
    }
}

Nesse caso, nosso controlador deve obter o beanhomeService do contexto pai.

3.4. Hierarquia de Contexto

Agora podemos colocar tudo junto e definir a hierarquia de contexto usandoSpringApplicationBuilder:

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);
    }
}

Finalmente, ao executar o Spring Boot App, podemos acessar ambos os aplicativos em suas respectivas portas usandolocalhost:8081/ctx1/home andlocalhost:8082/ctx2/greeting.

4. Conclusão

Usando a APISpringApplicationBuilder, primeiro criamos um relacionamento pai-filho entre dois contextos de um aplicativo. Em seguida, abordamos como substituir a configuração pai no contexto filho. Por fim, adicionamos um contexto irmão para demonstrar como a configuração no contexto pai pode ser compartilhada com outros contextos filhos.

O código-fonte do exemplo está disponívelover on GitHub.