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;
}
}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
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.
Referência: System requirements · Spring Boot 4.1.1 / Spring Framework 7.0.9 / Java 25