← Spring Boot®: aplicações, contratos e operação
01 / 10 · 60 MIN

Contexto da aplicação e dependências

Interpreta como o contexto encontra, cria e escolhe dependências; distingue auto-configuração, composição explícita e estado partilhado.

Uma base explícita para ler a aplicação

Este percurso usa Spring Boot 4.1.1, Spring Framework 7.0.9 e Java 25. A documentação de requisitos admite Java 17 a 26 nessa versão de Boot; escolher Java 25 mantém a ligação ao percurso de linguagem. As perguntas avaliam interpretação de código e decisões. Os exemplos não exigem instalar ferramentas nem executar programas para responder.

Spring Boot coordena configuração e integrações sobre Spring. @SpringBootApplication reúne configuração da aplicação, auto-configuração e component scan. Não define, por si só, o contrato de todos os serviços externos nem ativa todos os perfis. Uma aplicação que arrancou ainda precisa de cumprir os requisitos das operações que oferece.

O classpath e o scan são fronteiras diferentes

Uma classe pode existir no classpath sem ser registada como componente. O scan implícito começa no pacote da classe de arranque e percorre o âmbito respetivo. Se a classe principal está em com.acme.orders e um serviço em com.acme.billing, o pacote irmão não passa a ser incluído só porque pertence ao mesmo repositório.

Escolhe um pacote raiz intencional para a aplicação ou importa configurações específicas dos módulos. Colocar a classe no pacote default pode levar à leitura de classes de todos os jars e tornar o âmbito imprevisível. A correção de um bean em falta deve explicar por que a definição passa a ser incluída, sem esconder a dependência obrigatória.

O construtor apresenta o contrato de composição

Um serviço que precisa de Inventory deve receber essa dependência no construtor. Num componente gerido com um único construtor, @Autowired não é obrigatório. Um campo final pode conservar a referência escolhida na construção. Isso não significa que o objeto referido seja imutável nem que chamadas externas nunca falhem.

A composição explícita ajuda a perceber o que falta no arranque e permite fornecer uma alternativa controlada numa revisão ou teste. Fazer new diretamente cria uma instância Java; não a transforma automaticamente no bean gerido com a mesma classe. Quando se comparam dois comportamentos, identifica qual instância foi usada e de onde veio a sua dependência.

Escolher um candidato sem alterar todos os consumidores

Se existem vários beans compatíveis com o tipo pedido, a aplicação precisa de uma regra de seleção coerente. Um qualificador no parâmetro restringe esse conjunto para o consumidor específico. No exemplo, bulk identifica o candidato pretendido; o outro Channel continua disponível a consumidores com requisitos diferentes.

@Primary pode definir uma preferência geral quando é adequado fazê-lo. Mudar essa preferência para resolver um único consumidor pode alterar consumidores existentes sem qualificador. Antes de mudar a configuração global, distingue um novo requisito local de uma decisão global sobre o adaptador predefinido. O qualificador continua a operar dentro dos candidatos compatíveis com o tipo.

Auto-configuração é uma decisão condicionada

A auto-configuração considera as dependências disponíveis e outras condições, incluindo beans já definidos. Uma base embebida pode ser configurada quando existem as dependências adequadas e não foi fornecido outro DataSource. Ao acrescentar um DataSource próprio, essa parte da configuração predefinida pode recuar. Isso não significa desligar toda a auto-configuração.

Usa o relatório de condições para explicar o que foi aplicado e porquê. --debug permite obter esse relatório no diagnóstico normal, mas produzir o relatório não corrige a composição. Para personalizar, usa os pontos documentados; o nome público de uma classe de auto-configuração pode servir para exclusão sem tornar os seus métodos internos uma API estável.

Partilha de instância não é isolamento de pedidos

O scope singleton é por contexto e definição de bean. Consumidores do mesmo bean partilham a instância. Não é uma promessa de uma única instância da classe em todas as aplicações da JVM e não fornece serialização automática das chamadas. Um campo que guarda o tenant atual pode ser substituído por outro pedido antes de a primeira decisão terminar.

Mantém a identidade e os dados de cada pedido em argumentos ou variáveis locais quando não há motivo para os partilhar. Se existe estado realmente partilhado, define a política de concorrência e validade desse estado. Trocar um campo por volatile pode melhorar visibilidade, mas não torna exclusiva uma sequência de várias operações de negócio.

Leitura guiada da composição

O exemplo apresenta três ficheiros no mesmo pacote raiz. DispatchApplication define duas fábricas Channel. DispatchPlanner é detetado como serviço e pede o candidato bulk no único construtor. A chamada describe com um identificador válido para o consumidor produz uma descrição com o destino archive; nenhuma entrega externa acontece nesse método.

Segue o que mudaria se o serviço passasse para um pacote irmão fora do scan ou se o qualificador fosse retirado sem outra regra de seleção. São problemas diferentes: num caso falta descobrir o consumidor; no outro falta escolher entre dependências. O campo final guarda apenas Channel. O identificador de cada chamada continua local ao método.

// Separate files in package com.acme.dispatch.
// Spring Boot 4.1.1; Spring Framework 7.0.9; Java 25.
// DispatchApplication.java
package com.acme.dispatch;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class DispatchApplication {
  @Bean("interactive")
  Channel interactive() { return () -> "screen"; }
  @Bean("bulk")
  Channel bulk() { return () -> "archive"; }
  public static void main(String[] args) {
    SpringApplication.run(DispatchApplication.class, args);
  }
}

// Channel.java
package com.acme.dispatch;
public interface Channel { String destination(); }

// DispatchPlanner.java
package com.acme.dispatch;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class DispatchPlanner {
  private final Channel channel;
  public DispatchPlanner(@Qualifier("bulk") Channel channel) {
    this.channel = channel;
  }
  public String describe(String id) {
    return channel.destination() + ":" + id;
  }
}
NA PRÁTICA

Lê os três ficheiros como unidades separadas. As duas fábricas produzem Channel; o parâmetro qualificado escolhe bulk. describe apenas constrói texto e não entrega dados a um destino real. Não há estado de pedido guardado no serviço.

Armadilhas comuns

Confundir classpath com âmbito do scan; resolver uma dependência em falta com null; mudar um Primary global para um requisito local; guardar identidade do pedido num singleton mutável.

Tópicos relacionados: Configuração, binding e perfis · Âmbito de testes e evidência de integração · Empacotamento, entrega e aceitação operacional

Leva esta ideia contigo

O contexto gere objetos definidos, segundo condições e regras de seleção. Torna o âmbito, as dependências e a propriedade do estado explícitos.

Criar conta

Referência: System requirements · Spring Boot 4.1.1 / Spring Framework 7.0.9 / Java 25