Spring Boot e Spring Cloud
Configuração no Spring Boot: properties, YAML, profiles e @ConfigurationProperties
Iniciante9 min de leituraAtualizado
Esta página foi traduzida do inglês com a ajuda de IA e conferida com o original. Ler a versão em inglês
Você vai conseguir configurar um serviço Spring Boot 3 para vários ambientes sem recompilá-lo: escolher entre properties e YAML, entender qual fonte vence quando os valores conflitam, ativar profiles corretamente, fazer o binding das configurações para classes imutáveis e validadas, e manter os segredos fora do jar.
O que você vai aprender
- Ordenar as fontes de propriedades do Spring Boot e prever qual valor vence quando a mesma chave é definida duas vezes
- Escrever configuração específica de cada profile com arquivos application-{profile} e YAML com vários documentos
- Fazer o binding da configuração para records @ConfigurationProperties imutáveis e validados
- Usar o relaxed binding para que variáveis de ambiente, chaves YAML e argumentos de linha de comando apontem para a mesma propriedade
- Importar arquivos de configuração externos e manter as credenciais fora do artefato empacotado
Um serviço que precisa ser recompilado para mudar uma porta, uma URL de banco de dados ou uma feature flag não pode ser promovido de staging para produção como o mesmo artefato, e promover o mesmo artefato é justamente a razão de ser de um pipeline de deploy. A resposta do Spring Boot é a configuração externalizada: um único jar imutável e muitas fontes de propriedades empilhadas umas sobre as outras na inicialização, com uma ordem definida para resolver conflitos. Esta página trata dessa ordem, dos formatos de arquivo, dos profiles, do binding tipado e dos hábitos que evitam que a configuração vire uma sessão de depuração.
De onde vêm as propriedades
O Boot monta um único Environment a partir de vários PropertySources. Quando a mesma chave aparece em mais de um deles, vence o de maior precedência. A lista abaixo vai da menor para a maior precedência e inclui as fontes que você vai encontrar na prática (a documentação de referência traz a lista completa):
- Propriedades padrão definidas via código com
SpringApplication.setDefaultProperties - Anotações
@PropertySourceem classes@Configuration - Arquivos de config data:
application.properties/application.yml, depois as variantes específicas de profile - Um
RandomValuePropertySource(random.int,random.uuid) - Variáveis de ambiente do sistema operacional
- Propriedades de sistema do Java (
-Dserver.port=9090) - Atributos JNDI e parâmetros do servlet context e do servlet config
SPRING_APPLICATION_JSON(JSON inline em uma variável de ambiente ou propriedade de sistema)- Argumentos de linha de comando (
--server.port=9090) propertiesem@SpringBootTest,@TestPropertySourcee@DynamicPropertySourcenos testes- Configurações globais do DevTools na máquina do desenvolvedor
Duas consequências merecem ser bem assimiladas. Primeiro: tudo o que é definido na linha de comando ou no ambiente vence o que está dentro do jar, exatamente o que quem opera o serviço precisa. Segundo: @PropertySource é carregado tão tarde no processo que não consegue influenciar decisões iniciais, como a configuração do logging ou spring.profiles.active; é um recurso de compatibilidade, não o mecanismo principal.
Dentro da camada de config data, os arquivos também têm ordem. O Boot procura application.* nestes locais, e os posteriores sobrescrevem os anteriores:
- a raiz do classpath
- o pacote
/configno classpath - o diretório de trabalho atual
- um subdiretório
/configdo diretório de trabalho, e seus filhos imediatos
Assim, um arquivo colocado ao lado do jar em ./config/application.yml sobrescreve o que vem empacotado dentro dele, sem nenhuma flag. spring.config.name muda o nome base e spring.config.location substitui totalmente o caminho de busca (spring.config.additional-location acrescenta a ele). Os dois precisam ser informados cedo, como argumentos de linha de comando ou variáveis de ambiente, porque são lidos antes dos arquivos.
Properties ou YAML
Os dois formatos expressam a mesma árvore. Compare:
server.port=8081
spring.datasource.url=jdbc:postgresql://localhost:5432/shop
spring.datasource.username=shop
spring.jpa.open-in-view=false
app.allowed-origins[0]=https://shop.example.com
app.allowed-origins[1]=https://admin.example.com
server:
port: 8081
spring:
datasource:
url: jdbc:postgresql://localhost:5432/shop
username: shop
jpa:
open-in-view: false
app:
allowed-origins:
- https://shop.example.com
- https://admin.example.com
O YAML ganha em legibilidade assim que surgem aninhamentos e listas, e permite vários documentos em um mesmo arquivo, separados por ---. Em troca, é sensível à indentação e traz algumas surpresas no parsing: yes/no/on/off sem aspas viram booleanos em alguns parsers YAML, 01 pode ser lido como número octal e um valor que contenha dois-pontos precisa estar entre aspas. O Boot usa o SnakeYAML e precisa dele no classpath; os starters já o incluem. Note também que arquivos YAML não podem ser carregados via @PropertySource.
Placeholders funcionam nos dois: app.base-url=https://${app.host}:${server.port} é resolvido contra todo o Environment, e ${MY_VAR:default} fornece um valor alternativo.
Profiles
Um profile é uma fatia nomeada de configuração e de beans. Você ativa um ou mais com spring.profiles.active=prod (como propriedade, pela variável de ambiente SPRING_PROFILES_ACTIVE ou com --spring.profiles.active=prod). Quando nenhum profile está ativo, quem está ativo é o profile default.
Arquivos específicos de profile seguem a convenção de nome application-{profile}.yml, ficam nos mesmos locais que o application.yml e o sobrescrevem. Se vários profiles estiverem ativos, vence o último da lista. Dá para obter o mesmo efeito em um único arquivo YAML:
spring:
application:
name: shop
jpa:
open-in-view: false
---
spring:
config:
activate:
on-profile: local
datasource:
url: jdbc:h2:mem:shop
logging:
level:
org.hibernate.SQL: DEBUG
---
spring:
config:
activate:
on-profile: prod
datasource:
url: jdbc:postgresql://db.internal:5432/shop
server:
shutdown: graceful
A chave spring.config.activate.on-profile substituiu a antiga chave spring.profiles no Boot 2.4. Expressões de profile são permitidas: on-profile: "prod & eu" ou "!local".
Profiles também condicionam beans. @Profile("local") em uma @Configuration ou em um @Bean só o registra quando esse profile está ativo; @Profile("!prod") é a forma usual de manter um gateway de pagamento falso longe da produção. Use com parcimônia: um serviço cujo grafo de beans muda muito de um ambiente para outro é um serviço que você não testou de verdade antes do deploy.
Grupos de profiles permitem que um profile se expanda em vários: spring.profiles.group.prod=prod-db,prod-mq significa que ativar prod também ativa os outros dois. spring.profiles.include adiciona profiles incondicionalmente. Nenhum dos dois pode aparecer em um documento específico de profile, pelo motivo de ordenação explicado no FAQ.
Uma organização comum e sensata: um application.yml base com tudo o que é compartilhado, um arquivo de profile pequeno por ambiente contendo só o que muda, tudo versionado, e os segredos fornecidos de fora (veja abaixo). Nomes de ambiente como local, test, staging e prod são convenções, não palavras-chave; para o Boot, só a string importa.
Binding tipado com @ConfigurationProperties
@Value("${app.timeout}") serve para uma ou duas configurações. Daí em diante, ele espalha chaves em forma de string pelo código todo, sem validação e sem apoio da IDE. @ConfigurationProperties faz o binding de um prefixo inteiro para um objeto:
package com.example.shop.config;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Positive;
import java.time.Duration;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DurationUnit;
import org.springframework.validation.annotation.Validated;
@Validated
@ConfigurationProperties(prefix = "app")
public record AppProperties(
@NotEmpty List<String> allowedOrigins,
@Positive int maxPageSize,
Duration requestTimeout, // "30s", "2m", "500ms" all bind
Catalog catalog) {
public record Catalog(boolean cacheEnabled, Duration cacheTtl) {}
}
Para registrá-lo, basta uma destas duas anotações em uma classe de configuração: @EnableConfigurationProperties(AppProperties.class) para controle explícito, ou @ConfigurationPropertiesScan na classe da aplicação para registrar todas as classes anotadas da árvore de pacotes. Depois, injete AppProperties onde precisar, como qualquer outro bean.
Records trazem de graça o binding por construtor e a imutabilidade. No Boot 3, um record, ou uma classe com um único construtor com parâmetros, é vinculado automaticamente pelo construtor; a anotação explícita @ConstructorBinding do Boot 2 só é necessária quando a classe tem vários construtores. Um JavaBean mutável com setters continua funcionando quando você precisa de valores padrão que dependem de outros valores, mas records devem ser a escolha padrão.
@Validated ativa o Jakarta Bean Validation no objeto vinculado, de modo que a ausência de allowedOrigins faz a inicialização falhar com uma mensagem clara, indicando o valor rejeitado e a restrição. Falhar cedo é melhor do que descobrir um erro de digitação no nome de uma propriedade na primeira requisição em produção.
Adicione o spring-boot-configuration-processor como dependência opcional (annotationProcessor no Gradle) e o build gera META-INF/spring-configuration-metadata.json, que é o que dá autocompletar e documentação das suas próprias chaves no IntelliJ IDEA, no Eclipse e no VS Code. O Javadoc dos componentes do record vira a descrição exibida na IDE.
Relaxed binding
O Boot normaliza os nomes das chaves para que uma mesma propriedade possa ser escrita no estilo natural de cada fonte:
| Fonte | Grafia de app.max-page-size |
|---|---|
| Properties / YAML | app.max-page-size (kebab-case, a forma canônica), e também app.maxPageSize ou app.max_page_size |
| Variável de ambiente | APP_MAXPAGESIZE (maiúsculas, pontos viram underscores, hifens removidos) |
| Propriedade de sistema / linha de comando | app.max-page-size ou app.maxPageSize |
Para listas em variáveis de ambiente, os índices viram segmentos numéricos: APP_ALLOWEDORIGINS_0=https://shop.example.com. O relaxed binding vale para @ConfigurationProperties; @Value é mais rígido e espera o nome canônico exato.
Conversão
O binding entende durações (10s, PT10S, ou um número simples com @DurationUnit), tamanhos de dados (10MB, @DataSizeUnit), períodos, enums (sem diferenciar maiúsculas e minúsculas), File, Resource, InetAddress e qualquer tipo com um construtor String ou um método valueOf. Conversões personalizadas são registradas por meio de um bean Converter anotado com @ConfigurationPropertiesBinding.
Importando configuração e lidando com segredos
spring.config.import traz fontes adicionais para a camada de config data, processadas depois do arquivo que as declara:
spring.config.import=optional:file:./overrides.properties,optional:configtree:/run/secrets/
file:eclasspath:importam arquivos properties ou YAML comuns.optional:evita que a inicialização falhe quando o local não existe, que é o que você quer para um arquivo de sobrescrita usado só em desenvolvimento.configtree:lê um diretório em que o nome de cada arquivo é a chave e o conteúdo é o valor, a estrutura que o Kubernetes e o Docker produzem ao montar segredos. Um arquivo/run/secrets/db-passwordvira a propriedadedb-password.configserver:(com Spring Cloud Config) evault:(com Spring Cloud Vault) buscam a configuração em um repositório central.
Nada disso deve ficar dentro do jar: nem senhas, nem chaves de API, nem certificados privados. Forneça esses valores por variáveis de ambiente, arquivos de segredo montados ou um servidor de configuração, e referencie-os no application.yml com placeholders como password: ${DB_PASSWORD}. Se você realmente precisar guardar um valor criptografado em um arquivo, existem o prefixo {cipher} do Spring Cloud Config e as integrações com o Jasypt, mas um gerenciador de segredos é a ferramenta mais adequada.
Configurando o logging por propriedades
O logging é a configuração que quase todo mundo mexe primeiro, e o Boot expõe os casos comuns como propriedades, de modo que raramente é preciso um arquivo de configuração do logback:
logging.level.root=INFO
logging.level.com.example.shop=DEBUG
logging.level.org.springframework.web=DEBUG
logging.file.name=/var/log/shop/app.log
logging.pattern.console=%d{HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n
logging.logback.rollingpolicy.max-file-size=50MB
logging.file.name e logging.file.path substituíram as chaves logging.file e logging.path de antes da versão 2.2; os nomes antigos ainda aparecem em tutoriais e não funcionam mais. O Logback é a implementação padrão por trás dos starters; trocar para o Log4j2 significa excluir spring-boot-starter-logging e adicionar spring-boot-starter-log4j2. Como o logging é inicializado antes da maior parte do contexto, as propriedades logging.* precisam estar em um arquivo de config data, em uma variável de ambiente ou na linha de comando; um @PropertySource chega tarde demais.
Erros comuns
Achar que o arquivo de profile acrescenta itens à lista em vez de substituir o valor. Listas são substituídas por inteiro: um app.allowed-origins com duas entradas no application.yml e uma entrada no application-prod.yml resulta em uma única entrada em produção, não três. Maps, por outro lado, são mesclados chave a chave.
Definir spring.profiles.active no application.yml e não entender por que não dá para sobrescrevê-lo. Dá, sim: um argumento de linha de comando vence. O problema real é que um profile fixado dentro do artefato é mais uma coisa que quem opera o serviço precisa lembrar de sobrescrever. Deixe-o indefinido no arquivo empacotado e forneça-o pelo ambiente.
Usar uma classe interna sem static para as propriedades. O binder não consegue instanciar uma classe interna não estática; grupos aninhados devem ser classes aninhadas estáticas ou records.
Erro de digitação no nome de uma propriedade. O Boot ignora chaves desconhecidas silenciosamente: server.prot=9090 não faz nada. Habilite ignore-unknown-fields = false nos seus próprios @ConfigurationProperties, conte com os metadados da IDE para as chaves do Boot e prefira poucos prefixos com nomes bem escolhidos.
Passar segredos por SPRING_APPLICATION_JSON ou pela linha de comando. Os dois ficam visíveis para qualquer processo capaz de listar argumentos ou ler o ambiente da JVM; em hosts compartilhados, arquivos montados com permissões restritas são mais seguros.
Boas práticas
- Versione o
application.ymle todos os arquivosapplication-{profile}.yml; trate-os como código, com revisão e histórico. Mantenha o arquivo base completo e os arquivos de profile mínimos. - Agrupe configurações relacionadas em um record
@ConfigurationPropertiespor responsabilidade, valide-o e injete o record em vez de campos@Valueavulsos. - Adote a grafia canônica em kebab-case para as chaves nos arquivos; é a que os metadados e a documentação usam.
- Documente cada chave personalizada com Javadoc para que os metadados gerados a expliquem na IDE.
- Mantenha poucos profiles, com significado óbvio; se um profile só é ativado junto com outro, use um grupo em vez de pedir que todos se lembrem do par.
- Quando o comportamento muda entre ambientes, prefira uma propriedade lida por um único bean a trocar beans com
@Profile.
Principais pontos
- O Boot mescla várias fontes de propriedades em um único
Environment; argumentos de linha de comando e variáveis de ambiente sobrescrevem os arquivos dentro do jar, e arquivos ao lado do jar sobrescrevem os empacotados. - Properties e YAML são equivalentes; o YAML acrescenta arquivos com vários documentos e legibilidade, ao custo da indentação e de algumas armadilhas de parsing.
- Profiles selecionam configuração e beans; arquivos específicos de profile sobrescrevem o arquivo base, e
spring.profiles.activedeve ser fornecido de fora do artefato. - Records
@ConfigurationPropertiescom@Validatedoferecem configuração imutável, tipada e com suporte da IDE, que falha rápido diante de valores inválidos. - O relaxed binding mapeia
app.max-page-size,app.maxPageSizeeAPP_MAXPAGESIZEpara a mesma chave. spring.config.importcomoptional:econfigtree:é a forma limpa de trazer sobrescritas e segredos montados sem tocar no jar.
Fontes e leituras complementares
Perguntas frequentes
- Devo usar application.properties ou application.yml?
- Os dois são totalmente suportados e oferecem os mesmos recursos. O YAML é mais fácil de ler quando há chaves aninhadas e listas, e aceita vários documentos em um único arquivo, o que é prático para seções específicas de um profile. Arquivos properties são mais planos, mais difíceis de quebrar por causa de indentação e têm melhor suporte em algumas ferramentas. Escolha um por projeto; se os dois existirem com o mesmo nome, o arquivo .properties tem precedência.
- Por que minha variável de ambiente não sobrescreve o application.yml?
- Confira primeiro o nome: SPRING_DATASOURCE_URL é mapeada para spring.datasource.url graças ao relaxed binding, mas os segmentos precisam ser unidos por um único underscore e os hifens do nome da propriedade devem ser removidos. Depois confira o escopo: a variável precisa estar visível para o próprio processo da JVM, não só para o seu shell ou para uma etapa de build do Docker. Por fim, lembre-se de que argumentos de linha de comando e propriedades de teste têm precedência maior que variáveis de ambiente.
- Posso definir spring.profiles.active dentro de um arquivo específico de profile?
- Não. O Boot rejeita spring.profiles.active e spring.profiles.include em arquivos específicos de profile e em documentos YAML ativados por spring.config.activate.on-profile, porque um profile que ativa outros enquanto está sendo carregado tornaria o resultado dependente da ordem. Em vez disso, use spring.profiles.group no documento principal para definir um profile que se expande em vários outros.
- Como vejo o valor final de uma propriedade em tempo de execução?
- Com o Actuator, exponha o endpoint env e consulte /actuator/env/your.property.name; ele lista todas as fontes de propriedades que contêm a chave e qual delas venceu. No Boot 3 os valores são mascarados por padrão; defina management.endpoint.env.show-values=when-authorized (ou always, apenas em um ambiente protegido) para vê-los. Para uma verificação pontual, injete Environment e registre em log o getProperty em um CommandLineRunner.